blob: 8f7288dc590290fe40213b998b6a383b5a3147bc [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;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029};
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030
kthelgasonc8474172016-12-08 08:04:51 -080031// Round |value_to_round| to a multiple of |multiple|. Prefer rounding upwards,
32// but never more than |max_value|.
33int roundUp(int value_to_round, int multiple, int max_value) {
34 const int rounded_value =
35 (value_to_round + multiple - 1) / multiple * multiple;
36 return rounded_value <= max_value ? rounded_value
37 : (max_value / multiple * multiple);
magjed709f73c2016-05-13 10:26:00 -070038}
39
kthelgasonc8474172016-12-08 08:04:51 -080040// Generates a scale factor that makes |input_num_pixels| smaller or
41// larger than |target_num_pixels|, depending on the value of |step_up|.
42Fraction FindScale(int input_num_pixels, int target_num_pixels, bool step_up) {
43 // This function only makes sense for a positive target.
44 RTC_DCHECK_GT(target_num_pixels, 0);
45 Fraction best_scale = Fraction{1, 1};
46 Fraction last_scale = Fraction{1, 1};
47 const float target_scale =
48 sqrt(target_num_pixels / static_cast<float>(input_num_pixels));
49 while (best_scale.numerator > (target_scale * best_scale.denominator)) {
50 last_scale = best_scale;
51 if (best_scale.numerator % 3 == 0 && best_scale.denominator % 2 == 0) {
52 // Multiply by 2/3
53 best_scale.numerator /= 3;
54 best_scale.denominator /= 2;
55 } else {
56 // Multiply by 3/4
57 best_scale.numerator *= 3;
58 best_scale.denominator *= 4;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059 }
60 }
kthelgasonc8474172016-12-08 08:04:51 -080061 if (step_up)
62 return last_scale;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +000063 return best_scale;
64}
Per766ad3b2016-04-05 15:23:49 +020065} // namespace
henrike@webrtc.org28e20752013-07-10 00:45:36 +000066
Per766ad3b2016-04-05 15:23:49 +020067namespace cricket {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068
kthelgasonc8474172016-12-08 08:04:51 -080069VideoAdapter::VideoAdapter(int required_resolution_alignment)
magjed709f73c2016-05-13 10:26:00 -070070 : frames_in_(0),
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +000071 frames_out_(0),
72 frames_scaled_(0),
wu@webrtc.orgcadf9042013-08-30 21:24:16 +000073 adaption_changes_(0),
magjed@webrtc.orga73d7462014-11-14 13:25:25 +000074 previous_width_(0),
75 previous_height_(0),
kthelgasonc8474172016-12-08 08:04:51 -080076 required_resolution_alignment_(required_resolution_alignment),
magjed709f73c2016-05-13 10:26:00 -070077 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()),
kthelgasonc8474172016-12-08 08:04:51 -080078 step_up_(false) {}
79
80VideoAdapter::VideoAdapter() : VideoAdapter(1) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081
Per766ad3b2016-04-05 15:23:49 +020082VideoAdapter::~VideoAdapter() {}
83
magjed604abe02016-05-19 06:05:40 -070084bool VideoAdapter::KeepFrame(int64_t in_timestamp_ns) {
Per766ad3b2016-04-05 15:23:49 +020085 rtc::CritScope cs(&critical_section_);
magjed604abe02016-05-19 06:05:40 -070086 if (!requested_format_ || requested_format_->interval == 0)
87 return true;
88
89 if (next_frame_timestamp_ns_) {
90 // Time until next frame should be outputted.
91 const int64_t time_until_next_frame_ns =
92 (*next_frame_timestamp_ns_ - in_timestamp_ns);
93
94 // Continue if timestamp is withing expected range.
95 if (std::abs(time_until_next_frame_ns) < 2 * requested_format_->interval) {
96 // Drop if a frame shouldn't be outputted yet.
97 if (time_until_next_frame_ns > 0)
98 return false;
99 // Time to output new frame.
100 *next_frame_timestamp_ns_ += requested_format_->interval;
101 return true;
102 }
103 }
104
105 // First timestamp received or timestamp is way outside expected range, so
106 // reset. Set first timestamp target to just half the interval to prefer
107 // keeping frames in case of jitter.
108 next_frame_timestamp_ns_ =
109 rtc::Optional<int64_t>(in_timestamp_ns + requested_format_->interval / 2);
110 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111}
112
nisse47ac4622016-05-25 08:47:01 -0700113bool VideoAdapter::AdaptFrameResolution(int in_width,
magjed709f73c2016-05-13 10:26:00 -0700114 int in_height,
magjed604abe02016-05-19 06:05:40 -0700115 int64_t in_timestamp_ns,
magjed709f73c2016-05-13 10:26:00 -0700116 int* cropped_width,
117 int* cropped_height,
118 int* out_width,
119 int* out_height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000120 rtc::CritScope cs(&critical_section_);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000121 ++frames_in_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122
magjed709f73c2016-05-13 10:26:00 -0700123 // The max output pixel count is the minimum of the requests from
124 // OnOutputFormatRequest and OnResolutionRequest.
125 int max_pixel_count = resolution_request_max_pixel_count_;
126 if (requested_format_) {
kthelgasonc8474172016-12-08 08:04:51 -0800127 // TODO(kthelgason): remove the - |step_up_| hack when we change how
128 // resolution is requested from VideoSourceProxy.
129 // This is required because we must not scale above the requested
130 // format so we subtract one when scaling up.
magjed709f73c2016-05-13 10:26:00 -0700131 max_pixel_count = std::min(
kthelgasonc8474172016-12-08 08:04:51 -0800132 max_pixel_count, requested_format_->width * requested_format_->height -
133 static_cast<int>(step_up_));
magjed709f73c2016-05-13 10:26:00 -0700134 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135
136 // Drop the input frame if necessary.
kthelgasonc8474172016-12-08 08:04:51 -0800137 if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000138 // Show VAdapt log every 90 frames dropped. (3 seconds)
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000139 if ((frames_in_ - frames_out_) % 90 == 0) {
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000140 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed
141 // in default calls.
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000142 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_
143 << " / out " << frames_out_
144 << " / in " << frames_in_
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000145 << " Changes: " << adaption_changes_
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000146 << " Input: " << in_width
147 << "x" << in_height
magjed604abe02016-05-19 06:05:40 -0700148 << " timestamp: " << in_timestamp_ns
magjed709f73c2016-05-13 10:26:00 -0700149 << " Output: i"
150 << (requested_format_ ? requested_format_->interval : 0);
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000151 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000152
magjed709f73c2016-05-13 10:26:00 -0700153 // Drop frame.
nisse47ac4622016-05-25 08:47:01 -0700154 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155 }
156
magjed709f73c2016-05-13 10:26:00 -0700157 // Calculate how the input should be cropped.
158 if (!requested_format_ ||
159 requested_format_->width == 0 || requested_format_->height == 0) {
160 *cropped_width = in_width;
161 *cropped_height = in_height;
162 } else {
163 // Adjust |requested_format_| orientation to match input.
164 if ((in_width > in_height) !=
165 (requested_format_->width > requested_format_->height)) {
166 std::swap(requested_format_->width, requested_format_->height);
167 }
168 const float requested_aspect =
169 requested_format_->width /
170 static_cast<float>(requested_format_->height);
171 *cropped_width =
172 std::min(in_width, static_cast<int>(in_height * requested_aspect));
173 *cropped_height =
174 std::min(in_height, static_cast<int>(in_width / requested_aspect));
175 }
magjed709f73c2016-05-13 10:26:00 -0700176 const Fraction scale =
kthelgasonc8474172016-12-08 08:04:51 -0800177 FindScale(*cropped_width * *cropped_height, max_pixel_count, step_up_);
magjed709f73c2016-05-13 10:26:00 -0700178 // Adjust cropping slightly to get even integer output size and a perfect
kthelgasonc8474172016-12-08 08:04:51 -0800179 // scale factor. Make sure the resulting dimensions are aligned correctly
180 // to be nice to hardware encoders.
181 *cropped_width =
182 roundUp(*cropped_width,
183 scale.denominator * required_resolution_alignment_, in_width);
184 *cropped_height =
185 roundUp(*cropped_height,
186 scale.denominator * required_resolution_alignment_, in_height);
magjed709f73c2016-05-13 10:26:00 -0700187 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator);
188 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator);
189
190 // Calculate final output size.
191 *out_width = *cropped_width / scale.denominator * scale.numerator;
192 *out_height = *cropped_height / scale.denominator * scale.numerator;
kthelgasonc8474172016-12-08 08:04:51 -0800193 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
194 RTC_DCHECK_EQ(0, *out_height % required_resolution_alignment_);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000195
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000196 ++frames_out_;
magjed709f73c2016-05-13 10:26:00 -0700197 if (scale.numerator != scale.denominator)
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000198 ++frames_scaled_;
sergeyu@chromium.org9cf037b2014-02-07 19:03:26 +0000199
magjed709f73c2016-05-13 10:26:00 -0700200 if (previous_width_ && (previous_width_ != *out_width ||
201 previous_height_ != *out_height)) {
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000202 ++adaption_changes_;
Per766ad3b2016-04-05 15:23:49 +0200203 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out "
204 << frames_out_ << " / in " << frames_in_
205 << " Changes: " << adaption_changes_ << " Input: " << in_width
magjed604abe02016-05-19 06:05:40 -0700206 << "x" << in_height
magjed709f73c2016-05-13 10:26:00 -0700207 << " Scale: " << scale.numerator << "/" << scale.denominator
208 << " Output: " << *out_width << "x" << *out_height << " i"
209 << (requested_format_ ? requested_format_->interval : 0);
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000210 }
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000211
magjed709f73c2016-05-13 10:26:00 -0700212 previous_width_ = *out_width;
213 previous_height_ = *out_height;
nisse47ac4622016-05-25 08:47:01 -0700214
215 return true;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000216}
217
Per766ad3b2016-04-05 15:23:49 +0200218void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) {
219 rtc::CritScope cs(&critical_section_);
magjed709f73c2016-05-13 10:26:00 -0700220 requested_format_ = rtc::Optional<VideoFormat>(format);
magjed604abe02016-05-19 06:05:40 -0700221 next_frame_timestamp_ns_ = rtc::Optional<int64_t>();
henrike@webrtc.orgd43aa9d2014-02-21 23:43:24 +0000222}
223
Per766ad3b2016-04-05 15:23:49 +0200224void VideoAdapter::OnResolutionRequest(
perkj2d5f0912016-02-29 00:04:41 -0800225 rtc::Optional<int> max_pixel_count,
226 rtc::Optional<int> max_pixel_count_step_up) {
Per766ad3b2016-04-05 15:23:49 +0200227 rtc::CritScope cs(&critical_section_);
kthelgasonc8474172016-12-08 08:04:51 -0800228 resolution_request_max_pixel_count_ = max_pixel_count.value_or(
229 max_pixel_count_step_up.value_or(std::numeric_limits<int>::max()));
230 step_up_ = static_cast<bool>(max_pixel_count_step_up);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231}
232
233} // namespace cricket