blob: 522d175426bad752dc94134183c2c2de9af0c84b [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()),
lliuuf9ed2352017-03-30 10:44:38 -0700109 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()) {}
kthelgasonc8474172016-12-08 08:04:51 -0800110
111VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112
Per766ad3b2016-04-05 15:23:49 +0200113VideoAdapter::~VideoAdapter() {}
114
magjed604abe02016-05-19 06:05:40 -0700115bool VideoAdapter::KeepFrame(int64_t in_timestamp_ns) {
Per766ad3b2016-04-05 15:23:49 +0200116 rtc::CritScope cs(&critical_section_);
lliuuf9ed2352017-03-30 10:44:38 -0700117 if (!requested_format_ || requested_format_->interval == 0)
magjed604abe02016-05-19 06:05:40 -0700118 return true;
119
120 if (next_frame_timestamp_ns_) {
121 // Time until next frame should be outputted.
122 const int64_t time_until_next_frame_ns =
123 (*next_frame_timestamp_ns_ - in_timestamp_ns);
124
lliuuf9ed2352017-03-30 10:44:38 -0700125 // Continue if timestamp is withing expected range.
126 if (std::abs(time_until_next_frame_ns) < 2 * requested_format_->interval) {
magjed604abe02016-05-19 06:05:40 -0700127 // Drop if a frame shouldn't be outputted yet.
128 if (time_until_next_frame_ns > 0)
129 return false;
130 // Time to output new frame.
lliuuf9ed2352017-03-30 10:44:38 -0700131 *next_frame_timestamp_ns_ += requested_format_->interval;
magjed604abe02016-05-19 06:05:40 -0700132 return true;
133 }
134 }
135
136 // First timestamp received or timestamp is way outside expected range, so
137 // reset. Set first timestamp target to just half the interval to prefer
138 // keeping frames in case of jitter.
139 next_frame_timestamp_ns_ =
lliuuf9ed2352017-03-30 10:44:38 -0700140 rtc::Optional<int64_t>(in_timestamp_ns + requested_format_->interval / 2);
magjed604abe02016-05-19 06:05:40 -0700141 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142}
143
nisse47ac4622016-05-25 08:47:01 -0700144bool VideoAdapter::AdaptFrameResolution(int in_width,
magjed709f73c2016-05-13 10:26:00 -0700145 int in_height,
magjed604abe02016-05-19 06:05:40 -0700146 int64_t in_timestamp_ns,
magjed709f73c2016-05-13 10:26:00 -0700147 int* cropped_width,
148 int* cropped_height,
149 int* out_width,
150 int* out_height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000151 rtc::CritScope cs(&critical_section_);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000152 ++frames_in_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153
magjed709f73c2016-05-13 10:26:00 -0700154 // The max output pixel count is the minimum of the requests from
155 // OnOutputFormatRequest and OnResolutionRequest.
156 int max_pixel_count = resolution_request_max_pixel_count_;
157 if (requested_format_) {
158 max_pixel_count = std::min(
sprang84a37592017-02-10 07:04:27 -0800159 max_pixel_count, requested_format_->width * requested_format_->height);
magjed709f73c2016-05-13 10:26:00 -0700160 }
sprang84a37592017-02-10 07:04:27 -0800161 int target_pixel_count =
162 std::min(resolution_request_target_pixel_count_, max_pixel_count);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000163
164 // Drop the input frame if necessary.
kthelgasonc8474172016-12-08 08:04:51 -0800165 if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000166 // Show VAdapt log every 90 frames dropped. (3 seconds)
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000167 if ((frames_in_ - frames_out_) % 90 == 0) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000168 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
169 // in default calls.
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000170 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
171 << " / out " << frames_out_
172 << " / in " << frames_in_
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000173 << " Changes: " << adaption_changes_
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000174 << " Input: " << in_width
175 << "x" << in_height
magjed604abe02016-05-19 06:05:40 -0700176 << " timestamp: " << in_timestamp_ns
magjed709f73c2016-05-13 10:26:00 -0700177 << " Output: i"
178 << (requested_format_ ? requested_format_->interval : 0);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000179 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000180
magjed709f73c2016-05-13 10:26:00 -0700181 // Drop frame.
nisse47ac4622016-05-25 08:47:01 -0700182 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183 }
184
magjed709f73c2016-05-13 10:26:00 -0700185 // Calculate how the input should be cropped.
186 if (!requested_format_ ||
187 requested_format_->width == 0 || requested_format_->height == 0) {
188 *cropped_width = in_width;
189 *cropped_height = in_height;
190 } else {
191 // Adjust |requested_format_| orientation to match input.
192 if ((in_width > in_height) !=
193 (requested_format_->width > requested_format_->height)) {
194 std::swap(requested_format_->width, requested_format_->height);
195 }
196 const float requested_aspect =
197 requested_format_->width /
198 static_cast<float>(requested_format_->height);
199 *cropped_width =
200 std::min(in_width, static_cast<int>(in_height * requested_aspect));
201 *cropped_height =
202 std::min(in_height, static_cast<int>(in_width / requested_aspect));
203 }
sprang84a37592017-02-10 07:04:27 -0800204 const Fraction scale = FindScale((*cropped_width) * (*cropped_height),
205 target_pixel_count, max_pixel_count);
magjed709f73c2016-05-13 10:26:00 -0700206 // Adjust cropping slightly to get even integer output size and a perfect
kthelgasonc8474172016-12-08 08:04:51 -0800207 // scale factor. Make sure the resulting dimensions are aligned correctly
208 // to be nice to hardware encoders.
209 *cropped_width =
210 roundUp(*cropped_width,
211 scale.denominator * required_resolution_alignment_, in_width);
212 *cropped_height =
213 roundUp(*cropped_height,
214 scale.denominator * required_resolution_alignment_, in_height);
magjed709f73c2016-05-13 10:26:00 -0700215 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
216 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
217
218 // Calculate final output size.
219 *out_width = *cropped_width / scale.denominator * scale.numerator;
220 *out_height = *cropped_height / scale.denominator * scale.numerator;
magjed4e836822017-02-28 06:30:59 -0800221 RTC_DCHECK_EQ(0, *out_width % required_resolution_alignment_);
kthelgasonc8474172016-12-08 08:04:51 -0800222 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000223
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000224 ++frames_out_;
magjed709f73c2016-05-13 10:26:00 -0700225 if (scale.numerator != scale.denominator)
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000226 ++frames_scaled_;
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000227
magjed709f73c2016-05-13 10:26:00 -0700228 if (previous_width_ && (previous_width_ != *out_width ||
229 previous_height_ != *out_height)) {
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000230 ++adaption_changes_;
Per766ad3b2016-04-05 15:23:49 +0200231 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out "
232 << frames_out_ << " / in " << frames_in_
233 << " Changes: " << adaption_changes_ << " Input: " << in_width
magjed604abe02016-05-19 06:05:40 -0700234 << "x" << in_height
magjed709f73c2016-05-13 10:26:00 -0700235 << " Scale: " << scale.numerator << "/" << scale.denominator
236 << " Output: " << *out_width << "x" << *out_height << " i"
237 << (requested_format_ ? requested_format_->interval : 0);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000238 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000239
magjed709f73c2016-05-13 10:26:00 -0700240 previous_width_ = *out_width;
241 previous_height_ = *out_height;
nisse47ac4622016-05-25 08:47:01 -0700242
243 return true;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000244}
245
Per766ad3b2016-04-05 15:23:49 +0200246void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) {
247 rtc::CritScope cs(&critical_section_);
magjed709f73c2016-05-13 10:26:00 -0700248 requested_format_ = rtc::Optional<VideoFormat>(format);
magjed604abe02016-05-19 06:05:40 -0700249 next_frame_timestamp_ns_ = rtc::Optional<int64_t>();
henrike@webrtc.orgd43aa9d2014-02-21 23:43:24 +0000250}
251
lliuuf9ed2352017-03-30 10:44:38 -0700252void VideoAdapter::OnResolutionRequest(
sprang84a37592017-02-10 07:04:27 -0800253 const rtc::Optional<int>& target_pixel_count,
lliuuf9ed2352017-03-30 10:44:38 -0700254 const rtc::Optional<int>& max_pixel_count) {
Per766ad3b2016-04-05 15:23:49 +0200255 rtc::CritScope cs(&critical_section_);
lliuuf9ed2352017-03-30 10:44:38 -0700256 resolution_request_max_pixel_count_ =
257 max_pixel_count.value_or(std::numeric_limits<int>::max());
sprang84a37592017-02-10 07:04:27 -0800258 resolution_request_target_pixel_count_ =
259 target_pixel_count.value_or(resolution_request_max_pixel_count_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260}
261
262} // namespace cricket