blob: 840101bcee3403d64bb8f91f75c4d51ebd04b72d [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:131/*
kjellander1afca732016-02-08 04:46:452 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:133 *
kjellander1afca732016-02-08 04:46:454 * 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.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:139 */
henrike@webrtc.org28e20752013-07-10 00:45:3610
Mirko Bonadei92ea95e2017-09-15 04:47:3111#include "media/base/videoadapter.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
andresp@webrtc.orgff689be2015-02-12 11:54:2613#include <algorithm>
kthelgasonc8474172016-12-08 16:04:5114#include <cmath>
magjed604abe02016-05-19 13:05:4015#include <cstdlib>
Per766ad3b92016-04-05 13:23:4916#include <limits>
Steve Antone78bcb92017-10-31 16:53:0817#include <utility>
henrike@webrtc.org28e20752013-07-10 00:45:3618
Danil Chapovalov00c718362018-06-15 13:58:3819#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3120#include "media/base/mediaconstants.h"
21#include "media/base/videocommon.h"
22#include "rtc_base/arraysize.h"
23#include "rtc_base/checks.h"
24#include "rtc_base/logging.h"
henrike@webrtc.org28e20752013-07-10 00:45:3625
Per766ad3b92016-04-05 13:23:4926namespace {
magjed709f73c2016-05-13 17:26:0027struct Fraction {
28 int numerator;
29 int denominator;
sprang84a37592017-02-10 15:04:2730
31 // Determines number of output pixels if both width and height of an input of
32 // |input_pixels| pixels is scaled with the fraction numerator / denominator.
33 int scale_pixel_count(int input_pixels) {
34 return (numerator * numerator * input_pixels) / (denominator * denominator);
35 }
henrike@webrtc.org28e20752013-07-10 00:45:3636};
henrike@webrtc.org28e20752013-07-10 00:45:3637
kthelgasonc8474172016-12-08 16:04:5138// Round |value_to_round| to a multiple of |multiple|. Prefer rounding upwards,
39// but never more than |max_value|.
40int roundUp(int value_to_round, int multiple, int max_value) {
41 const int rounded_value =
42 (value_to_round + multiple - 1) / multiple * multiple;
43 return rounded_value <= max_value ? rounded_value
44 : (max_value / multiple * multiple);
magjed709f73c2016-05-13 17:26:0045}
46
sprang84a37592017-02-10 15:04:2747// Generates a scale factor that makes |input_pixels| close to |target_pixels|,
48// but no higher than |max_pixels|.
49Fraction FindScale(int input_pixels, int target_pixels, int max_pixels) {
kthelgasonc8474172016-12-08 16:04:5150 // This function only makes sense for a positive target.
sprang84a37592017-02-10 15:04:2751 RTC_DCHECK_GT(target_pixels, 0);
52 RTC_DCHECK_GT(max_pixels, 0);
53 RTC_DCHECK_GE(max_pixels, target_pixels);
54
55 // Don't scale up original.
56 if (target_pixels >= input_pixels)
57 return Fraction{1, 1};
58
59 Fraction current_scale = Fraction{1, 1};
kthelgasonc8474172016-12-08 16:04:5160 Fraction best_scale = Fraction{1, 1};
sprang84a37592017-02-10 15:04:2761 // The minimum (absolute) difference between the number of output pixels and
62 // the target pixel count.
63 int min_pixel_diff = std::numeric_limits<int>::max();
Magnus Jedvert6d230d72017-02-22 17:30:2764 if (input_pixels <= max_pixels) {
sprang84a37592017-02-10 15:04:2765 // Start condition for 1/1 case, if it is less than max.
66 min_pixel_diff = std::abs(input_pixels - target_pixels);
67 }
68
69 // Alternately scale down by 2/3 and 3/4. This results in fractions which are
70 // effectively scalable. For instance, starting at 1280x720 will result in
71 // the series (3/4) => 960x540, (1/2) => 640x360, (3/8) => 480x270,
72 // (1/4) => 320x180, (3/16) => 240x125, (1/8) => 160x90.
73 while (current_scale.scale_pixel_count(input_pixels) > target_pixels) {
74 if (current_scale.numerator % 3 == 0 &&
75 current_scale.denominator % 2 == 0) {
76 // Multiply by 2/3.
77 current_scale.numerator /= 3;
78 current_scale.denominator /= 2;
kthelgasonc8474172016-12-08 16:04:5179 } else {
sprang84a37592017-02-10 15:04:2780 // Multiply by 3/4.
81 current_scale.numerator *= 3;
82 current_scale.denominator *= 4;
83 }
84
85 int output_pixels = current_scale.scale_pixel_count(input_pixels);
86 if (output_pixels <= max_pixels) {
87 int diff = std::abs(target_pixels - output_pixels);
88 if (diff < min_pixel_diff) {
89 min_pixel_diff = diff;
90 best_scale = current_scale;
91 }
henrike@webrtc.org28e20752013-07-10 00:45:3692 }
93 }
sprang84a37592017-02-10 15:04:2794
wu@webrtc.orgcadf9042013-08-30 21:24:1695 return best_scale;
96}
Per766ad3b92016-04-05 13:23:4997} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:3698
Per766ad3b92016-04-05 13:23:4999namespace cricket {
henrike@webrtc.org28e20752013-07-10 00:45:36100
kthelgasonc8474172016-12-08 16:04:51101VideoAdapter::VideoAdapter(int required_resolution_alignment)
magjed709f73c2016-05-13 17:26:00102 : frames_in_(0),
sergeyu@chromium.org9cf037b2014-02-07 19:03:26103 frames_out_(0),
104 frames_scaled_(0),
wu@webrtc.orgcadf9042013-08-30 21:24:16105 adaption_changes_(0),
magjed@webrtc.orga73d7462014-11-14 13:25:25106 previous_width_(0),
107 previous_height_(0),
kthelgasonc8474172016-12-08 16:04:51108 required_resolution_alignment_(required_resolution_alignment),
sprang84a37592017-02-10 15:04:27109 resolution_request_target_pixel_count_(std::numeric_limits<int>::max()),
sprangc5d62e22017-04-03 06:53:04110 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
111 max_framerate_request_(std::numeric_limits<int>::max()) {}
kthelgasonc8474172016-12-08 16:04:51112
113VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
henrike@webrtc.org28e20752013-07-10 00:45:36114
Per766ad3b92016-04-05 13:23:49115VideoAdapter::~VideoAdapter() {}
116
magjed604abe02016-05-19 13:05:40117bool VideoAdapter::KeepFrame(int64_t in_timestamp_ns) {
Per766ad3b92016-04-05 13:23:49118 rtc::CritScope cs(&critical_section_);
sprangc5d62e22017-04-03 06:53:04119
Åsa Persson2e4419e2018-09-06 13:02:55120 int max_fps = max_framerate_request_;
121 if (max_fps_)
122 max_fps = std::min(max_fps, *max_fps_);
123
124 if (max_fps <= 0)
125 return false;
sprangc5d62e22017-04-03 06:53:04126
127 // If |max_framerate_request_| is not set, it will default to maxint, which
128 // will lead to a frame_interval_ns rounded to 0.
Åsa Persson2e4419e2018-09-06 13:02:55129 int64_t frame_interval_ns = rtc::kNumNanosecsPerSec / max_fps;
sprangc5d62e22017-04-03 06:53:04130 if (frame_interval_ns <= 0) {
131 // Frame rate throttling not enabled.
magjed604abe02016-05-19 13:05:40132 return true;
sprangc5d62e22017-04-03 06:53:04133 }
magjed604abe02016-05-19 13:05:40134
135 if (next_frame_timestamp_ns_) {
136 // Time until next frame should be outputted.
137 const int64_t time_until_next_frame_ns =
138 (*next_frame_timestamp_ns_ - in_timestamp_ns);
139
sprangc5d62e22017-04-03 06:53:04140 // Continue if timestamp is within expected range.
141 if (std::abs(time_until_next_frame_ns) < 2 * frame_interval_ns) {
magjed604abe02016-05-19 13:05:40142 // Drop if a frame shouldn't be outputted yet.
143 if (time_until_next_frame_ns > 0)
144 return false;
145 // Time to output new frame.
sprangc5d62e22017-04-03 06:53:04146 *next_frame_timestamp_ns_ += frame_interval_ns;
magjed604abe02016-05-19 13:05:40147 return true;
148 }
149 }
150
151 // First timestamp received or timestamp is way outside expected range, so
152 // reset. Set first timestamp target to just half the interval to prefer
153 // keeping frames in case of jitter.
Oskar Sundbom78807582017-11-16 10:09:55154 next_frame_timestamp_ns_ = in_timestamp_ns + frame_interval_ns / 2;
magjed604abe02016-05-19 13:05:40155 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36156}
157
nisse47ac4622016-05-25 15:47:01158bool VideoAdapter::AdaptFrameResolution(int in_width,
magjed709f73c2016-05-13 17:26:00159 int in_height,
magjed604abe02016-05-19 13:05:40160 int64_t in_timestamp_ns,
magjed709f73c2016-05-13 17:26:00161 int* cropped_width,
162 int* cropped_height,
163 int* out_width,
164 int* out_height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52165 rtc::CritScope cs(&critical_section_);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26166 ++frames_in_;
henrike@webrtc.org28e20752013-07-10 00:45:36167
magjed709f73c2016-05-13 17:26:00168 // The max output pixel count is the minimum of the requests from
Åsa Persson2e4419e2018-09-06 13:02:55169 // OnOutputFormatRequest and OnResolutionFramerateRequest.
magjed709f73c2016-05-13 17:26:00170 int max_pixel_count = resolution_request_max_pixel_count_;
Åsa Persson2e4419e2018-09-06 13:02:55171
172 if (max_pixel_count_)
173 max_pixel_count = std::min(max_pixel_count, *max_pixel_count_);
174
sprang84a37592017-02-10 15:04:27175 int target_pixel_count =
176 std::min(resolution_request_target_pixel_count_, max_pixel_count);
henrike@webrtc.org28e20752013-07-10 00:45:36177
178 // Drop the input frame if necessary.
kthelgasonc8474172016-12-08 16:04:51179 if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26180 // Show VAdapt log every 90 frames dropped. (3 seconds)
wu@webrtc.orgb9a088b2014-02-13 23:18:49181 if ((frames_in_ - frames_out_) % 90 == 0) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26182 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
183 // in default calls.
Mirko Bonadei675513b2017-11-09 10:09:25184 RTC_LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
185 << " / out " << frames_out_ << " / in " << frames_in_
186 << " Changes: " << adaption_changes_
187 << " Input: " << in_width << "x" << in_height
Åsa Persson2e4419e2018-09-06 13:02:55188 << " timestamp: " << in_timestamp_ns
189 << " Output fps: " << max_framerate_request_ << "/"
190 << max_fps_.value_or(-1);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26191 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14192
magjed709f73c2016-05-13 17:26:00193 // Drop frame.
nisse47ac4622016-05-25 15:47:01194 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36195 }
196
magjed709f73c2016-05-13 17:26:00197 // Calculate how the input should be cropped.
Åsa Persson2e4419e2018-09-06 13:02:55198 if (!target_aspect_ratio_ || target_aspect_ratio_->first <= 0 ||
199 target_aspect_ratio_->second <= 0) {
magjed709f73c2016-05-13 17:26:00200 *cropped_width = in_width;
201 *cropped_height = in_height;
202 } else {
Åsa Persson2e4419e2018-09-06 13:02:55203 // Adjust |target_aspect_ratio_| orientation to match input.
magjed709f73c2016-05-13 17:26:00204 if ((in_width > in_height) !=
Åsa Persson2e4419e2018-09-06 13:02:55205 (target_aspect_ratio_->first > target_aspect_ratio_->second)) {
206 std::swap(target_aspect_ratio_->first, target_aspect_ratio_->second);
magjed709f73c2016-05-13 17:26:00207 }
208 const float requested_aspect =
Åsa Persson2e4419e2018-09-06 13:02:55209 target_aspect_ratio_->first /
210 static_cast<float>(target_aspect_ratio_->second);
magjed709f73c2016-05-13 17:26:00211 *cropped_width =
212 std::min(in_width, static_cast<int>(in_height * requested_aspect));
213 *cropped_height =
214 std::min(in_height, static_cast<int>(in_width / requested_aspect));
215 }
sprang84a37592017-02-10 15:04:27216 const Fraction scale = FindScale((*cropped_width) * (*cropped_height),
217 target_pixel_count, max_pixel_count);
magjed709f73c2016-05-13 17:26:00218 // Adjust cropping slightly to get even integer output size and a perfect
kthelgasonc8474172016-12-08 16:04:51219 // scale factor. Make sure the resulting dimensions are aligned correctly
220 // to be nice to hardware encoders.
221 *cropped_width =
222 roundUp(*cropped_width,
223 scale.denominator * required_resolution_alignment_, in_width);
224 *cropped_height =
225 roundUp(*cropped_height,
226 scale.denominator * required_resolution_alignment_, in_height);
magjed709f73c2016-05-13 17:26:00227 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
228 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
229
230 // Calculate final output size.
231 *out_width = *cropped_width / scale.denominator * scale.numerator;
232 *out_height = *cropped_height / scale.denominator * scale.numerator;
magjed4e836822017-02-28 14:30:59233 RTC_DCHECK_EQ(0, *out_width % required_resolution_alignment_);
kthelgasonc8474172016-12-08 16:04:51234 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
wu@webrtc.orgcadf9042013-08-30 21:24:16235
sergeyu@chromium.org9cf037b2014-02-07 19:03:26236 ++frames_out_;
magjed709f73c2016-05-13 17:26:00237 if (scale.numerator != scale.denominator)
sergeyu@chromium.org9cf037b2014-02-07 19:03:26238 ++frames_scaled_;
sergeyu@chromium.org9cf037b2014-02-07 19:03:26239
Yves Gerey665174f2018-06-19 13:03:05240 if (previous_width_ &&
241 (previous_width_ != *out_width || previous_height_ != *out_height)) {
wu@webrtc.orgcadf9042013-08-30 21:24:16242 ++adaption_changes_;
Mirko Bonadei675513b2017-11-09 10:09:25243 RTC_LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_
244 << " / out " << frames_out_ << " / in " << frames_in_
245 << " Changes: " << adaption_changes_
246 << " Input: " << in_width << "x" << in_height
247 << " Scale: " << scale.numerator << "/"
248 << scale.denominator << " Output: " << *out_width << "x"
Åsa Persson2e4419e2018-09-06 13:02:55249 << *out_height << " fps: " << max_framerate_request_ << "/"
250 << max_fps_.value_or(-1);
wu@webrtc.orgcadf9042013-08-30 21:24:16251 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14252
magjed709f73c2016-05-13 17:26:00253 previous_width_ = *out_width;
254 previous_height_ = *out_height;
nisse47ac4622016-05-25 15:47:01255
256 return true;
magjed@webrtc.orgf58b4552014-11-19 18:09:14257}
258
Chris Dziemborowicz048805e2018-03-02 01:55:53259void VideoAdapter::OnOutputFormatRequest(
Danil Chapovalov00c718362018-06-15 13:58:38260 const absl::optional<VideoFormat>& format) {
Åsa Persson2e4419e2018-09-06 13:02:55261 absl::optional<std::pair<int, int>> target_aspect_ratio;
262 absl::optional<int> max_pixel_count;
263 absl::optional<int> max_fps;
264 if (format) {
265 target_aspect_ratio = std::make_pair(format->width, format->height);
266 max_pixel_count = format->width * format->height;
267 if (format->interval > 0)
268 max_fps = rtc::kNumNanosecsPerSec / format->interval;
269 }
270 OnOutputFormatRequest(target_aspect_ratio, max_pixel_count, max_fps);
271}
272
273void VideoAdapter::OnOutputFormatRequest(
274 const absl::optional<std::pair<int, int>>& target_aspect_ratio,
275 const absl::optional<int>& max_pixel_count,
276 const absl::optional<int>& max_fps) {
Per766ad3b92016-04-05 13:23:49277 rtc::CritScope cs(&critical_section_);
Åsa Persson2e4419e2018-09-06 13:02:55278 target_aspect_ratio_ = target_aspect_ratio;
279 max_pixel_count_ = max_pixel_count;
280 max_fps_ = max_fps;
Danil Chapovalov00c718362018-06-15 13:58:38281 next_frame_timestamp_ns_ = absl::nullopt;
henrike@webrtc.orgd43aa9d2014-02-21 23:43:24282}
283
sprangc5d62e22017-04-03 06:53:04284void VideoAdapter::OnResolutionFramerateRequest(
Danil Chapovalov00c718362018-06-15 13:58:38285 const absl::optional<int>& target_pixel_count,
sprangc5d62e22017-04-03 06:53:04286 int max_pixel_count,
287 int max_framerate_fps) {
Per766ad3b92016-04-05 13:23:49288 rtc::CritScope cs(&critical_section_);
sprangc5d62e22017-04-03 06:53:04289 resolution_request_max_pixel_count_ = max_pixel_count;
sprang84a37592017-02-10 15:04:27290 resolution_request_target_pixel_count_ =
291 target_pixel_count.value_or(resolution_request_max_pixel_count_);
sprangc5d62e22017-04-03 06:53:04292 max_framerate_request_ = max_framerate_fps;
henrike@webrtc.org28e20752013-07-10 00:45:36293}
294
295} // namespace cricket