blob: 7fb02e15fe864f3fabe7257093b8d04576f6558c [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * 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:13 +00009 */
henrike@webrtc.org28e20752013-07-10 00:45:36 +000010
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "media/base/videoadapter.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000013#include <algorithm>
kthelgasonc8474172016-12-08 08:04:51 -080014#include <cmath>
magjed604abe02016-05-19 06:05:40 -070015#include <cstdlib>
Per766ad3b2016-04-05 15:23:49 +020016#include <limits>
Steve Antone78bcb92017-10-31 09:53:08 -070017#include <utility>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000018
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "api/optional.h"
20#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:36 +000025
Per766ad3b2016-04-05 15:23:49 +020026namespace {
magjed709f73c2016-05-13 10:26:00 -070027struct Fraction {
28 int numerator;
29 int denominator;
sprang84a37592017-02-10 07:04:27 -080030
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:36 +000036};
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037
kthelgasonc8474172016-12-08 08:04:51 -080038// 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 10:26:00 -070045}
46
sprang84a37592017-02-10 07:04:27 -080047// 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 08:04:51 -080050 // This function only makes sense for a positive target.
sprang84a37592017-02-10 07:04:27 -080051 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 08:04:51 -080060 Fraction best_scale = Fraction{1, 1};
sprang84a37592017-02-10 07:04:27 -080061 // 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 18:30:27 +010064 if (input_pixels <= max_pixels) {
sprang84a37592017-02-10 07:04:27 -080065 // 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 08:04:51 -080079 } else {
sprang84a37592017-02-10 07:04:27 -080080 // 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:36 +000092 }
93 }
sprang84a37592017-02-10 07:04:27 -080094
wu@webrtc.orgcadf9042013-08-30 21:24:16 +000095 return best_scale;
96}
Per766ad3b2016-04-05 15:23:49 +020097} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000098
Per766ad3b2016-04-05 15:23:49 +020099namespace cricket {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100
kthelgasonc8474172016-12-08 08:04:51 -0800101VideoAdapter::VideoAdapter(int required_resolution_alignment)
magjed709f73c2016-05-13 10:26:00 -0700102 : frames_in_(0),
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000103 frames_out_(0),
104 frames_scaled_(0),
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000105 adaption_changes_(0),
magjed@webrtc.orga73d7462014-11-14 13:25:25 +0000106 previous_width_(0),
107 previous_height_(0),
kthelgasonc8474172016-12-08 08:04:51 -0800108 required_resolution_alignment_(required_resolution_alignment),
sprang84a37592017-02-10 07:04:27 -0800109 resolution_request_target_pixel_count_(std::numeric_limits<int>::max()),
sprangc5d62e22017-04-02 23:53:04 -0700110 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
111 max_framerate_request_(std::numeric_limits<int>::max()) {}
kthelgasonc8474172016-12-08 08:04:51 -0800112
113VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114
Per766ad3b2016-04-05 15:23:49 +0200115VideoAdapter::~VideoAdapter() {}
116
magjed604abe02016-05-19 06:05:40 -0700117bool VideoAdapter::KeepFrame(int64_t in_timestamp_ns) {
Per766ad3b2016-04-05 15:23:49 +0200118 rtc::CritScope cs(&critical_section_);
sprangc5d62e22017-04-02 23:53:04 -0700119 if (max_framerate_request_ <= 0)
120 return false;
121
122 int64_t frame_interval_ns =
123 requested_format_ ? requested_format_->interval : 0;
124
125 // If |max_framerate_request_| is not set, it will default to maxint, which
126 // will lead to a frame_interval_ns rounded to 0.
127 frame_interval_ns = std::max<int64_t>(
128 frame_interval_ns, rtc::kNumNanosecsPerSec / max_framerate_request_);
129
130 if (frame_interval_ns <= 0) {
131 // Frame rate throttling not enabled.
magjed604abe02016-05-19 06:05:40 -0700132 return true;
sprangc5d62e22017-04-02 23:53:04 -0700133 }
magjed604abe02016-05-19 06:05:40 -0700134
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-02 23:53:04 -0700140 // Continue if timestamp is within expected range.
141 if (std::abs(time_until_next_frame_ns) < 2 * frame_interval_ns) {
magjed604abe02016-05-19 06:05:40 -0700142 // 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-02 23:53:04 -0700146 *next_frame_timestamp_ns_ += frame_interval_ns;
magjed604abe02016-05-19 06:05:40 -0700147 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.
154 next_frame_timestamp_ns_ =
sprangc5d62e22017-04-02 23:53:04 -0700155 rtc::Optional<int64_t>(in_timestamp_ns + frame_interval_ns / 2);
magjed604abe02016-05-19 06:05:40 -0700156 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000157}
158
nisse47ac4622016-05-25 08:47:01 -0700159bool VideoAdapter::AdaptFrameResolution(int in_width,
magjed709f73c2016-05-13 10:26:00 -0700160 int in_height,
magjed604abe02016-05-19 06:05:40 -0700161 int64_t in_timestamp_ns,
magjed709f73c2016-05-13 10:26:00 -0700162 int* cropped_width,
163 int* cropped_height,
164 int* out_width,
165 int* out_height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000166 rtc::CritScope cs(&critical_section_);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000167 ++frames_in_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000168
magjed709f73c2016-05-13 10:26:00 -0700169 // The max output pixel count is the minimum of the requests from
170 // OnOutputFormatRequest and OnResolutionRequest.
171 int max_pixel_count = resolution_request_max_pixel_count_;
172 if (requested_format_) {
173 max_pixel_count = std::min(
sprang84a37592017-02-10 07:04:27 -0800174 max_pixel_count, requested_format_->width * requested_format_->height);
magjed709f73c2016-05-13 10:26:00 -0700175 }
sprang84a37592017-02-10 07:04:27 -0800176 int target_pixel_count =
177 std::min(resolution_request_target_pixel_count_, max_pixel_count);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000178
179 // Drop the input frame if necessary.
kthelgasonc8474172016-12-08 08:04:51 -0800180 if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000181 // Show VAdapt log every 90 frames dropped. (3 seconds)
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000182 if ((frames_in_ - frames_out_) % 90 == 0) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000183 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
184 // in default calls.
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000185 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
186 << " / out " << frames_out_
187 << " / in " << frames_in_
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000188 << " Changes: " << adaption_changes_
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000189 << " Input: " << in_width
190 << "x" << in_height
magjed604abe02016-05-19 06:05:40 -0700191 << " timestamp: " << in_timestamp_ns
magjed709f73c2016-05-13 10:26:00 -0700192 << " Output: i"
193 << (requested_format_ ? requested_format_->interval : 0);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000194 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000195
magjed709f73c2016-05-13 10:26:00 -0700196 // Drop frame.
nisse47ac4622016-05-25 08:47:01 -0700197 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000198 }
199
magjed709f73c2016-05-13 10:26:00 -0700200 // Calculate how the input should be cropped.
201 if (!requested_format_ ||
202 requested_format_->width == 0 || requested_format_->height == 0) {
203 *cropped_width = in_width;
204 *cropped_height = in_height;
205 } else {
206 // Adjust |requested_format_| orientation to match input.
207 if ((in_width > in_height) !=
208 (requested_format_->width > requested_format_->height)) {
209 std::swap(requested_format_->width, requested_format_->height);
210 }
211 const float requested_aspect =
212 requested_format_->width /
213 static_cast<float>(requested_format_->height);
214 *cropped_width =
215 std::min(in_width, static_cast<int>(in_height * requested_aspect));
216 *cropped_height =
217 std::min(in_height, static_cast<int>(in_width / requested_aspect));
218 }
sprang84a37592017-02-10 07:04:27 -0800219 const Fraction scale = FindScale((*cropped_width) * (*cropped_height),
220 target_pixel_count, max_pixel_count);
magjed709f73c2016-05-13 10:26:00 -0700221 // Adjust cropping slightly to get even integer output size and a perfect
kthelgasonc8474172016-12-08 08:04:51 -0800222 // scale factor. Make sure the resulting dimensions are aligned correctly
223 // to be nice to hardware encoders.
224 *cropped_width =
225 roundUp(*cropped_width,
226 scale.denominator * required_resolution_alignment_, in_width);
227 *cropped_height =
228 roundUp(*cropped_height,
229 scale.denominator * required_resolution_alignment_, in_height);
magjed709f73c2016-05-13 10:26:00 -0700230 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
231 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
232
233 // Calculate final output size.
234 *out_width = *cropped_width / scale.denominator * scale.numerator;
235 *out_height = *cropped_height / scale.denominator * scale.numerator;
magjed4e836822017-02-28 06:30:59 -0800236 RTC_DCHECK_EQ(0, *out_width % required_resolution_alignment_);
kthelgasonc8474172016-12-08 08:04:51 -0800237 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000238
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000239 ++frames_out_;
magjed709f73c2016-05-13 10:26:00 -0700240 if (scale.numerator != scale.denominator)
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000241 ++frames_scaled_;
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000242
magjed709f73c2016-05-13 10:26:00 -0700243 if (previous_width_ && (previous_width_ != *out_width ||
244 previous_height_ != *out_height)) {
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000245 ++adaption_changes_;
Per766ad3b2016-04-05 15:23:49 +0200246 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out "
247 << frames_out_ << " / in " << frames_in_
248 << " Changes: " << adaption_changes_ << " Input: " << in_width
magjed604abe02016-05-19 06:05:40 -0700249 << "x" << in_height
magjed709f73c2016-05-13 10:26:00 -0700250 << " Scale: " << scale.numerator << "/" << scale.denominator
251 << " Output: " << *out_width << "x" << *out_height << " i"
252 << (requested_format_ ? requested_format_->interval : 0);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000253 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000254
magjed709f73c2016-05-13 10:26:00 -0700255 previous_width_ = *out_width;
256 previous_height_ = *out_height;
nisse47ac4622016-05-25 08:47:01 -0700257
258 return true;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000259}
260
Per766ad3b2016-04-05 15:23:49 +0200261void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) {
262 rtc::CritScope cs(&critical_section_);
magjed709f73c2016-05-13 10:26:00 -0700263 requested_format_ = rtc::Optional<VideoFormat>(format);
magjed604abe02016-05-19 06:05:40 -0700264 next_frame_timestamp_ns_ = rtc::Optional<int64_t>();
henrike@webrtc.orgd43aa9d2014-02-21 23:43:24 +0000265}
266
sprangc5d62e22017-04-02 23:53:04 -0700267void VideoAdapter::OnResolutionFramerateRequest(
sprang84a37592017-02-10 07:04:27 -0800268 const rtc::Optional<int>& target_pixel_count,
sprangc5d62e22017-04-02 23:53:04 -0700269 int max_pixel_count,
270 int max_framerate_fps) {
Per766ad3b2016-04-05 15:23:49 +0200271 rtc::CritScope cs(&critical_section_);
sprangc5d62e22017-04-02 23:53:04 -0700272 resolution_request_max_pixel_count_ = max_pixel_count;
sprang84a37592017-02-10 07:04:27 -0800273 resolution_request_target_pixel_count_ =
274 target_pixel_count.value_or(resolution_request_max_pixel_count_);
sprangc5d62e22017-04-02 23:53:04 -0700275 max_framerate_request_ = max_framerate_fps;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276}
277
278} // namespace cricket