blob: bee2a5730161e4e926006f8a8b89aa1e1ce90518 [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
kjellandera96e2d72016-02-04 23:52:28 -080011#include "webrtc/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>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017
kthelgasonc8474172016-12-08 08:04:51 -080018#include "webrtc/base/arraysize.h"
magjed709f73c2016-05-13 10:26:00 -070019#include "webrtc/base/checks.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000020#include "webrtc/base/logging.h"
kthelgasonc8474172016-12-08 08:04:51 -080021#include "webrtc/base/optional.h"
kjellanderf4752772016-03-02 05:42:30 -080022#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080023#include "webrtc/media/base/videocommon.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000024
Per766ad3b2016-04-05 15:23:49 +020025namespace {
magjed709f73c2016-05-13 10:26:00 -070026struct Fraction {
27 int numerator;
28 int denominator;
sprang84a37592017-02-10 07:04:27 -080029
30 // Determines number of output pixels if both width and height of an input of
31 // |input_pixels| pixels is scaled with the fraction numerator / denominator.
32 int scale_pixel_count(int input_pixels) {
33 return (numerator * numerator * input_pixels) / (denominator * denominator);
34 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035};
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036
kthelgasonc8474172016-12-08 08:04:51 -080037// Round |value_to_round| to a multiple of |multiple|. Prefer rounding upwards,
38// but never more than |max_value|.
39int roundUp(int value_to_round, int multiple, int max_value) {
40 const int rounded_value =
41 (value_to_round + multiple - 1) / multiple * multiple;
42 return rounded_value <= max_value ? rounded_value
43 : (max_value / multiple * multiple);
magjed709f73c2016-05-13 10:26:00 -070044}
45
sprang84a37592017-02-10 07:04:27 -080046// Generates a scale factor that makes |input_pixels| close to |target_pixels|,
47// but no higher than |max_pixels|.
48Fraction FindScale(int input_pixels, int target_pixels, int max_pixels) {
kthelgasonc8474172016-12-08 08:04:51 -080049 // This function only makes sense for a positive target.
sprang84a37592017-02-10 07:04:27 -080050 RTC_DCHECK_GT(target_pixels, 0);
51 RTC_DCHECK_GT(max_pixels, 0);
52 RTC_DCHECK_GE(max_pixels, target_pixels);
53
54 // Don't scale up original.
55 if (target_pixels >= input_pixels)
56 return Fraction{1, 1};
57
58 Fraction current_scale = Fraction{1, 1};
kthelgasonc8474172016-12-08 08:04:51 -080059 Fraction best_scale = Fraction{1, 1};
sprang84a37592017-02-10 07:04:27 -080060 // The minimum (absolute) difference between the number of output pixels and
61 // the target pixel count.
62 int min_pixel_diff = std::numeric_limits<int>::max();
Magnus Jedvert6d230d72017-02-22 18:30:27 +010063 if (input_pixels <= max_pixels) {
sprang84a37592017-02-10 07:04:27 -080064 // Start condition for 1/1 case, if it is less than max.
65 min_pixel_diff = std::abs(input_pixels - target_pixels);
66 }
67
68 // Alternately scale down by 2/3 and 3/4. This results in fractions which are
69 // effectively scalable. For instance, starting at 1280x720 will result in
70 // the series (3/4) => 960x540, (1/2) => 640x360, (3/8) => 480x270,
71 // (1/4) => 320x180, (3/16) => 240x125, (1/8) => 160x90.
72 while (current_scale.scale_pixel_count(input_pixels) > target_pixels) {
73 if (current_scale.numerator % 3 == 0 &&
74 current_scale.denominator % 2 == 0) {
75 // Multiply by 2/3.
76 current_scale.numerator /= 3;
77 current_scale.denominator /= 2;
kthelgasonc8474172016-12-08 08:04:51 -080078 } else {
sprang84a37592017-02-10 07:04:27 -080079 // Multiply by 3/4.
80 current_scale.numerator *= 3;
81 current_scale.denominator *= 4;
82 }
83
84 int output_pixels = current_scale.scale_pixel_count(input_pixels);
85 if (output_pixels <= max_pixels) {
86 int diff = std::abs(target_pixels - output_pixels);
87 if (diff < min_pixel_diff) {
88 min_pixel_diff = diff;
89 best_scale = current_scale;
90 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091 }
92 }
sprang84a37592017-02-10 07:04:27 -080093
wu@webrtc.orgcadf9042013-08-30 21:24:16 +000094 return best_scale;
95}
Per766ad3b2016-04-05 15:23:49 +020096} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097
Per766ad3b2016-04-05 15:23:49 +020098namespace cricket {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000099
kthelgasonc8474172016-12-08 08:04:51 -0800100VideoAdapter::VideoAdapter(int required_resolution_alignment)
magjed709f73c2016-05-13 10:26:00 -0700101 : frames_in_(0),
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000102 frames_out_(0),
103 frames_scaled_(0),
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000104 adaption_changes_(0),
magjed@webrtc.orga73d7462014-11-14 13:25:25 +0000105 previous_width_(0),
106 previous_height_(0),
kthelgasonc8474172016-12-08 08:04:51 -0800107 required_resolution_alignment_(required_resolution_alignment),
sprang84a37592017-02-10 07:04:27 -0800108 resolution_request_target_pixel_count_(std::numeric_limits<int>::max()),
sprang3ea3c772017-03-30 07:23:48 -0700109 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
110 max_framerate_request_(std::numeric_limits<int>::max()) {}
kthelgasonc8474172016-12-08 08:04:51 -0800111
112VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113
Per766ad3b2016-04-05 15:23:49 +0200114VideoAdapter::~VideoAdapter() {}
115
magjed604abe02016-05-19 06:05:40 -0700116bool VideoAdapter::KeepFrame(int64_t in_timestamp_ns) {
Per766ad3b2016-04-05 15:23:49 +0200117 rtc::CritScope cs(&critical_section_);
sprang3ea3c772017-03-30 07:23:48 -0700118 if (max_framerate_request_ <= 0)
119 return false;
120
121 int64_t frame_interval_ns =
122 requested_format_ ? requested_format_->interval : 0;
123
124 // If |max_framerate_request_| is not set, it will default to maxint, which
125 // will lead to a frame_interval_ns rounded to 0.
126 frame_interval_ns = std::max<int64_t>(
127 frame_interval_ns, rtc::kNumNanosecsPerSec / max_framerate_request_);
128
129 if (frame_interval_ns <= 0) {
130 // Frame rate throttling not enabled.
magjed604abe02016-05-19 06:05:40 -0700131 return true;
sprang3ea3c772017-03-30 07:23:48 -0700132 }
magjed604abe02016-05-19 06:05:40 -0700133
134 if (next_frame_timestamp_ns_) {
135 // Time until next frame should be outputted.
136 const int64_t time_until_next_frame_ns =
137 (*next_frame_timestamp_ns_ - in_timestamp_ns);
138
sprang3ea3c772017-03-30 07:23:48 -0700139 // Continue if timestamp is within expected range.
140 if (std::abs(time_until_next_frame_ns) < 2 * frame_interval_ns) {
magjed604abe02016-05-19 06:05:40 -0700141 // Drop if a frame shouldn't be outputted yet.
142 if (time_until_next_frame_ns > 0)
143 return false;
144 // Time to output new frame.
sprang3ea3c772017-03-30 07:23:48 -0700145 *next_frame_timestamp_ns_ += frame_interval_ns;
magjed604abe02016-05-19 06:05:40 -0700146 return true;
147 }
148 }
149
150 // First timestamp received or timestamp is way outside expected range, so
151 // reset. Set first timestamp target to just half the interval to prefer
152 // keeping frames in case of jitter.
153 next_frame_timestamp_ns_ =
sprang3ea3c772017-03-30 07:23:48 -0700154 rtc::Optional<int64_t>(in_timestamp_ns + frame_interval_ns / 2);
magjed604abe02016-05-19 06:05:40 -0700155 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000156}
157
nisse47ac4622016-05-25 08:47:01 -0700158bool VideoAdapter::AdaptFrameResolution(int in_width,
magjed709f73c2016-05-13 10:26:00 -0700159 int in_height,
magjed604abe02016-05-19 06:05:40 -0700160 int64_t in_timestamp_ns,
magjed709f73c2016-05-13 10:26:00 -0700161 int* cropped_width,
162 int* cropped_height,
163 int* out_width,
164 int* out_height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000165 rtc::CritScope cs(&critical_section_);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000166 ++frames_in_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000167
magjed709f73c2016-05-13 10:26:00 -0700168 // The max output pixel count is the minimum of the requests from
169 // OnOutputFormatRequest and OnResolutionRequest.
170 int max_pixel_count = resolution_request_max_pixel_count_;
171 if (requested_format_) {
172 max_pixel_count = std::min(
sprang84a37592017-02-10 07:04:27 -0800173 max_pixel_count, requested_format_->width * requested_format_->height);
magjed709f73c2016-05-13 10:26:00 -0700174 }
sprang84a37592017-02-10 07:04:27 -0800175 int target_pixel_count =
176 std::min(resolution_request_target_pixel_count_, max_pixel_count);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000177
178 // Drop the input frame if necessary.
kthelgasonc8474172016-12-08 08:04:51 -0800179 if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000180 // Show VAdapt log every 90 frames dropped. (3 seconds)
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000181 if ((frames_in_ - frames_out_) % 90 == 0) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000182 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
183 // in default calls.
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000184 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
185 << " / out " << frames_out_
186 << " / in " << frames_in_
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000187 << " Changes: " << adaption_changes_
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000188 << " Input: " << in_width
189 << "x" << in_height
magjed604abe02016-05-19 06:05:40 -0700190 << " timestamp: " << in_timestamp_ns
magjed709f73c2016-05-13 10:26:00 -0700191 << " Output: i"
192 << (requested_format_ ? requested_format_->interval : 0);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000193 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000194
magjed709f73c2016-05-13 10:26:00 -0700195 // Drop frame.
nisse47ac4622016-05-25 08:47:01 -0700196 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197 }
198
magjed709f73c2016-05-13 10:26:00 -0700199 // Calculate how the input should be cropped.
200 if (!requested_format_ ||
201 requested_format_->width == 0 || requested_format_->height == 0) {
202 *cropped_width = in_width;
203 *cropped_height = in_height;
204 } else {
205 // Adjust |requested_format_| orientation to match input.
206 if ((in_width > in_height) !=
207 (requested_format_->width > requested_format_->height)) {
208 std::swap(requested_format_->width, requested_format_->height);
209 }
210 const float requested_aspect =
211 requested_format_->width /
212 static_cast<float>(requested_format_->height);
213 *cropped_width =
214 std::min(in_width, static_cast<int>(in_height * requested_aspect));
215 *cropped_height =
216 std::min(in_height, static_cast<int>(in_width / requested_aspect));
217 }
sprang84a37592017-02-10 07:04:27 -0800218 const Fraction scale = FindScale((*cropped_width) * (*cropped_height),
219 target_pixel_count, max_pixel_count);
magjed709f73c2016-05-13 10:26:00 -0700220 // Adjust cropping slightly to get even integer output size and a perfect
kthelgasonc8474172016-12-08 08:04:51 -0800221 // scale factor. Make sure the resulting dimensions are aligned correctly
222 // to be nice to hardware encoders.
223 *cropped_width =
224 roundUp(*cropped_width,
225 scale.denominator * required_resolution_alignment_, in_width);
226 *cropped_height =
227 roundUp(*cropped_height,
228 scale.denominator * required_resolution_alignment_, in_height);
magjed709f73c2016-05-13 10:26:00 -0700229 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
230 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
231
232 // Calculate final output size.
233 *out_width = *cropped_width / scale.denominator * scale.numerator;
234 *out_height = *cropped_height / scale.denominator * scale.numerator;
magjed4e836822017-02-28 06:30:59 -0800235 RTC_DCHECK_EQ(0, *out_width % required_resolution_alignment_);
kthelgasonc8474172016-12-08 08:04:51 -0800236 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000237
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000238 ++frames_out_;
magjed709f73c2016-05-13 10:26:00 -0700239 if (scale.numerator != scale.denominator)
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000240 ++frames_scaled_;
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000241
magjed709f73c2016-05-13 10:26:00 -0700242 if (previous_width_ && (previous_width_ != *out_width ||
243 previous_height_ != *out_height)) {
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000244 ++adaption_changes_;
Per766ad3b2016-04-05 15:23:49 +0200245 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out "
246 << frames_out_ << " / in " << frames_in_
247 << " Changes: " << adaption_changes_ << " Input: " << in_width
magjed604abe02016-05-19 06:05:40 -0700248 << "x" << in_height
magjed709f73c2016-05-13 10:26:00 -0700249 << " Scale: " << scale.numerator << "/" << scale.denominator
250 << " Output: " << *out_width << "x" << *out_height << " i"
251 << (requested_format_ ? requested_format_->interval : 0);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000252 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000253
magjed709f73c2016-05-13 10:26:00 -0700254 previous_width_ = *out_width;
255 previous_height_ = *out_height;
nisse47ac4622016-05-25 08:47:01 -0700256
257 return true;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000258}
259
Per766ad3b2016-04-05 15:23:49 +0200260void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) {
261 rtc::CritScope cs(&critical_section_);
magjed709f73c2016-05-13 10:26:00 -0700262 requested_format_ = rtc::Optional<VideoFormat>(format);
magjed604abe02016-05-19 06:05:40 -0700263 next_frame_timestamp_ns_ = rtc::Optional<int64_t>();
henrike@webrtc.orgd43aa9d2014-02-21 23:43:24 +0000264}
265
sprang3ea3c772017-03-30 07:23:48 -0700266void VideoAdapter::OnResolutionFramerateRequest(
sprang84a37592017-02-10 07:04:27 -0800267 const rtc::Optional<int>& target_pixel_count,
sprang3ea3c772017-03-30 07:23:48 -0700268 int max_pixel_count,
269 int max_framerate_fps) {
Per766ad3b2016-04-05 15:23:49 +0200270 rtc::CritScope cs(&critical_section_);
sprang3ea3c772017-03-30 07:23:48 -0700271 resolution_request_max_pixel_count_ = max_pixel_count;
sprang84a37592017-02-10 07:04:27 -0800272 resolution_request_target_pixel_count_ =
273 target_pixel_count.value_or(resolution_request_max_pixel_count_);
sprang3ea3c772017-03-30 07:23:48 -0700274 max_framerate_request_ = max_framerate_fps;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275}
276
277} // namespace cricket