OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/media/base/videoadapter.h" | 11 #include "webrtc/media/base/videoadapter.h" |
12 | 12 |
13 #include <algorithm> | 13 #include <algorithm> |
14 #include <limits> | 14 #include <limits> |
15 | 15 |
| 16 #include "webrtc/base/checks.h" |
16 #include "webrtc/base/logging.h" | 17 #include "webrtc/base/logging.h" |
17 #include "webrtc/media/base/mediaconstants.h" | 18 #include "webrtc/media/base/mediaconstants.h" |
18 #include "webrtc/media/base/videocommon.h" | 19 #include "webrtc/media/base/videocommon.h" |
19 | 20 |
20 namespace { | 21 namespace { |
21 | 22 |
| 23 struct Fraction { |
| 24 int numerator; |
| 25 int denominator; |
| 26 }; |
| 27 |
22 // Scale factors optimized for in libYUV that we accept. | 28 // Scale factors optimized for in libYUV that we accept. |
23 // Must be sorted in decreasing scale factors for FindScaleLargerThan to work. | 29 // Must be sorted in decreasing scale factors for FindScaleLargerThan to work. |
24 const float kScaleFactors[] = { | 30 const Fraction kScaleFractions[] = { |
25 1.f / 1.f, // Full size. | 31 {1, 1}, |
26 3.f / 4.f, // 3/4 scale. | 32 {3, 4}, |
27 1.f / 2.f, // 1/2 scale. | 33 {1, 2}, |
28 3.f / 8.f, // 3/8 scale. | 34 {3, 8}, |
29 1.f / 4.f, // 1/4 scale. | 35 {1, 4}, |
30 3.f / 16.f, // 3/16 scale. | 36 {3, 16}, |
31 }; | 37 }; |
32 | 38 |
33 float FindScaleLessThanOrEqual(int width, | 39 // Round |valueToRound| to a multiple of |multiple|. Prefer rounding upwards, |
34 int height, | 40 // but never more than |maxValue|. |
35 int target_num_pixels, | 41 int roundUp(int valueToRound, int multiple, int maxValue) { |
36 int* resulting_number_of_pixels) { | 42 const int roundedValue = (valueToRound + multiple - 1) / multiple * multiple; |
| 43 return roundedValue <= maxValue ? roundedValue |
| 44 : (maxValue / multiple * multiple); |
| 45 } |
| 46 |
| 47 Fraction FindScaleLessThanOrEqual(int input_num_pixels, int target_num_pixels) { |
37 float best_distance = std::numeric_limits<float>::max(); | 48 float best_distance = std::numeric_limits<float>::max(); |
38 float best_scale = 0.0f; // Default to 0 if nothing matches. | 49 Fraction best_scale = {0, 1}; // Default to 0 if nothing matches. |
39 float pixels = width * height; | 50 for (const auto& fraction : kScaleFractions) { |
40 float best_number_of_pixels = 0.0f; | 51 const float scale = |
41 for (const auto& scale : kScaleFactors) { | 52 fraction.numerator / static_cast<float>(fraction.denominator); |
42 float test_num_pixels = pixels * scale * scale; | 53 float test_num_pixels = input_num_pixels * scale * scale; |
43 float diff = target_num_pixels - test_num_pixels; | 54 float diff = target_num_pixels - test_num_pixels; |
44 if (diff < 0) { | 55 if (diff < 0) { |
45 continue; | 56 continue; |
46 } | 57 } |
47 if (diff < best_distance) { | 58 if (diff < best_distance) { |
48 best_distance = diff; | 59 best_distance = diff; |
49 best_scale = scale; | 60 best_scale = fraction; |
50 best_number_of_pixels = test_num_pixels; | |
51 if (best_distance == 0) { // Found exact match. | 61 if (best_distance == 0) { // Found exact match. |
52 break; | 62 break; |
53 } | 63 } |
54 } | 64 } |
55 } | 65 } |
56 if (resulting_number_of_pixels) { | |
57 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); | |
58 } | |
59 return best_scale; | 66 return best_scale; |
60 } | 67 } |
61 | 68 |
62 float FindScaleLargerThan(int width, | 69 Fraction FindScaleLargerThan(int input_num_pixels, |
63 int height, | 70 int target_num_pixels, |
64 int target_num_pixels, | 71 int* resulting_number_of_pixels) { |
65 int* resulting_number_of_pixels) { | |
66 float best_distance = std::numeric_limits<float>::max(); | 72 float best_distance = std::numeric_limits<float>::max(); |
67 float best_scale = 1.f; // Default to unscaled if nothing matches. | 73 Fraction best_scale = {1, 1}; // Default to unscaled if nothing matches. |
68 float pixels = width * height; | 74 // Default to input number of pixels. |
69 float best_number_of_pixels = pixels; // Default to input number of pixels. | 75 float best_number_of_pixels = input_num_pixels; |
70 for (const auto& scale : kScaleFactors) { | 76 for (const auto& fraction : kScaleFractions) { |
71 float test_num_pixels = pixels * scale * scale; | 77 const float scale = |
| 78 fraction.numerator / static_cast<float>(fraction.denominator); |
| 79 float test_num_pixels = input_num_pixels * scale * scale; |
72 float diff = test_num_pixels - target_num_pixels; | 80 float diff = test_num_pixels - target_num_pixels; |
73 if (diff <= 0) { | 81 if (diff <= 0) { |
74 break; | 82 break; |
75 } | 83 } |
76 if (diff < best_distance) { | 84 if (diff < best_distance) { |
77 best_distance = diff; | 85 best_distance = diff; |
78 best_scale = scale; | 86 best_scale = fraction; |
79 best_number_of_pixels = test_num_pixels; | 87 best_number_of_pixels = test_num_pixels; |
80 } | 88 } |
81 } | 89 } |
82 | 90 |
83 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); | 91 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); |
84 return best_scale; | 92 return best_scale; |
85 } | 93 } |
86 | 94 |
| 95 Fraction FindScale(int input_num_pixels, |
| 96 int max_pixel_count_step_up, |
| 97 int max_pixel_count) { |
| 98 // Try scale just above |max_pixel_count_step_up_|. |
| 99 if (max_pixel_count_step_up > 0) { |
| 100 int resulting_pixel_count; |
| 101 const Fraction scale = FindScaleLargerThan( |
| 102 input_num_pixels, max_pixel_count_step_up, &resulting_pixel_count); |
| 103 if (resulting_pixel_count <= max_pixel_count) |
| 104 return scale; |
| 105 } |
| 106 // Return largest scale below |max_pixel_count|. |
| 107 return FindScaleLessThanOrEqual(input_num_pixels, max_pixel_count); |
| 108 } |
| 109 |
87 } // namespace | 110 } // namespace |
88 | 111 |
89 namespace cricket { | 112 namespace cricket { |
90 | 113 |
91 VideoAdapter::VideoAdapter() | 114 VideoAdapter::VideoAdapter() |
92 : output_num_pixels_(std::numeric_limits<int>::max()), | 115 : frames_in_(0), |
93 frames_in_(0), | |
94 frames_out_(0), | 116 frames_out_(0), |
95 frames_scaled_(0), | 117 frames_scaled_(0), |
96 adaption_changes_(0), | 118 adaption_changes_(0), |
97 previous_width_(0), | 119 previous_width_(0), |
98 previous_height_(0), | 120 previous_height_(0), |
| 121 input_interval_(0), |
99 interval_next_frame_(0), | 122 interval_next_frame_(0), |
100 format_request_max_pixel_count_(std::numeric_limits<int>::max()), | 123 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()), |
101 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()) {} | 124 resolution_request_max_pixel_count_step_up_(0) {} |
102 | 125 |
103 VideoAdapter::~VideoAdapter() {} | 126 VideoAdapter::~VideoAdapter() {} |
104 | 127 |
105 void VideoAdapter::SetExpectedInputFrameInterval(int64_t interval) { | 128 void VideoAdapter::SetExpectedInputFrameInterval(int64_t interval) { |
106 // TODO(perkj): Consider measuring input frame rate instead. | 129 // TODO(perkj): Consider measuring input frame rate instead. |
107 // Frame rate typically varies depending on lighting. | 130 // Frame rate typically varies depending on lighting. |
108 rtc::CritScope cs(&critical_section_); | 131 rtc::CritScope cs(&critical_section_); |
109 input_format_.interval = interval; | 132 input_interval_ = interval; |
110 } | 133 } |
111 | 134 |
112 void VideoAdapter::SetInputFormat(const VideoFormat& format) { | 135 void VideoAdapter::AdaptFrameResolution(int in_width, |
113 bool is_resolution_change = (input_format().width != format.width || | 136 int in_height, |
114 input_format().height != format.height); | 137 int* cropped_width, |
115 int64_t old_input_interval = input_format_.interval; | 138 int* cropped_height, |
116 input_format_ = format; | 139 int* out_width, |
117 output_format_.interval = | 140 int* out_height) { |
118 std::max(output_format_.interval, input_format_.interval); | |
119 if (old_input_interval != input_format_.interval) { | |
120 LOG(LS_INFO) << "VAdapt input interval changed from " | |
121 << old_input_interval << " to " << input_format_.interval; | |
122 } | |
123 if (is_resolution_change) { | |
124 // Trigger the adaptation logic again, to potentially reset the adaptation | |
125 // state for things like view requests that may not longer be capping | |
126 // output (or may now cap output). | |
127 Adapt(std::min(format_request_max_pixel_count_, | |
128 resolution_request_max_pixel_count_), | |
129 0); | |
130 } | |
131 } | |
132 | |
133 const VideoFormat& VideoAdapter::input_format() const { | |
134 rtc::CritScope cs(&critical_section_); | |
135 return input_format_; | |
136 } | |
137 | |
138 VideoFormat VideoAdapter::AdaptFrameResolution(int in_width, int in_height) { | |
139 rtc::CritScope cs(&critical_section_); | 141 rtc::CritScope cs(&critical_section_); |
140 ++frames_in_; | 142 ++frames_in_; |
141 | 143 |
142 SetInputFormat(VideoFormat( | 144 // The max output pixel count is the minimum of the requests from |
143 in_width, in_height, input_format_.interval, input_format_.fourcc)); | 145 // OnOutputFormatRequest and OnResolutionRequest. |
| 146 int max_pixel_count = resolution_request_max_pixel_count_; |
| 147 if (requested_format_) { |
| 148 max_pixel_count = std::min( |
| 149 max_pixel_count, requested_format_->width * requested_format_->height); |
| 150 } |
144 | 151 |
145 // Drop the input frame if necessary. | 152 // Drop the input frame if necessary. |
146 bool should_drop = false; | 153 bool should_drop = false; |
147 if (!output_num_pixels_) { | 154 if (max_pixel_count == 0) { |
148 // Drop all frames as the output format is 0x0. | 155 // Drop all frames as the output format is 0x0. |
149 should_drop = true; | 156 should_drop = true; |
150 } else { | 157 } else if (requested_format_ && requested_format_->interval > 0) { |
151 // Drop some frames based on input fps and output fps. | 158 // Drop some frames based on input fps and output fps. |
152 // Normally output fps is less than input fps. | 159 // Normally output fps is less than input fps. |
153 interval_next_frame_ += input_format_.interval; | 160 interval_next_frame_ += input_interval_; |
154 if (output_format_.interval > 0) { | 161 if (interval_next_frame_ >= requested_format_->interval) { |
155 if (interval_next_frame_ >= output_format_.interval) { | 162 interval_next_frame_ -= requested_format_->interval; |
156 interval_next_frame_ %= output_format_.interval; | 163 // Reset |interval_next_frame_| if it accumulates too much to avoid |
157 } else { | 164 // "catching up" behaviour. |
158 should_drop = true; | 165 if (interval_next_frame_ >= requested_format_->interval) |
159 } | 166 interval_next_frame_ = 0; |
| 167 } else { |
| 168 should_drop = true; |
160 } | 169 } |
161 } | 170 } |
162 if (should_drop) { | 171 if (should_drop) { |
163 // Show VAdapt log every 90 frames dropped. (3 seconds) | 172 // Show VAdapt log every 90 frames dropped. (3 seconds) |
164 if ((frames_in_ - frames_out_) % 90 == 0) { | 173 if ((frames_in_ - frames_out_) % 90 == 0) { |
165 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed | 174 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed |
166 // in default calls. | 175 // in default calls. |
167 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ | 176 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ |
168 << " / out " << frames_out_ | 177 << " / out " << frames_out_ |
169 << " / in " << frames_in_ | 178 << " / in " << frames_in_ |
170 << " Changes: " << adaption_changes_ | 179 << " Changes: " << adaption_changes_ |
171 << " Input: " << in_width | 180 << " Input: " << in_width |
172 << "x" << in_height | 181 << "x" << in_height |
173 << " i" << input_format_.interval | 182 << " i" << input_interval_ |
174 << " Output: i" << output_format_.interval; | 183 << " Output: i" |
| 184 << (requested_format_ ? requested_format_->interval : 0); |
175 } | 185 } |
176 | 186 |
177 return VideoFormat(); // Drop frame. | 187 // Drop frame. |
| 188 *cropped_width = 0; |
| 189 *cropped_height = 0; |
| 190 *out_width = 0; |
| 191 *out_height = 0; |
| 192 return; |
178 } | 193 } |
179 | 194 |
180 const float scale = FindScaleLessThanOrEqual(in_width, in_height, | 195 // Calculate how the input should be cropped. |
181 output_num_pixels_, nullptr); | 196 if (!requested_format_ || |
182 const int output_width = static_cast<int>(in_width * scale + .5f); | 197 requested_format_->width == 0 || requested_format_->height == 0) { |
183 const int output_height = static_cast<int>(in_height * scale + .5f); | 198 *cropped_width = in_width; |
| 199 *cropped_height = in_height; |
| 200 } else { |
| 201 // Adjust |requested_format_| orientation to match input. |
| 202 if ((in_width > in_height) != |
| 203 (requested_format_->width > requested_format_->height)) { |
| 204 std::swap(requested_format_->width, requested_format_->height); |
| 205 } |
| 206 const float requested_aspect = |
| 207 requested_format_->width / |
| 208 static_cast<float>(requested_format_->height); |
| 209 *cropped_width = |
| 210 std::min(in_width, static_cast<int>(in_height * requested_aspect)); |
| 211 *cropped_height = |
| 212 std::min(in_height, static_cast<int>(in_width / requested_aspect)); |
| 213 } |
| 214 |
| 215 // Find best scale factor. |
| 216 const Fraction scale = |
| 217 FindScale(*cropped_width * *cropped_height, |
| 218 resolution_request_max_pixel_count_step_up_, max_pixel_count); |
| 219 |
| 220 // Adjust cropping slightly to get even integer output size and a perfect |
| 221 // scale factor. |
| 222 *cropped_width = roundUp(*cropped_width, scale.denominator, in_width); |
| 223 *cropped_height = roundUp(*cropped_height, scale.denominator, in_height); |
| 224 RTC_DCHECK_EQ(0, *cropped_width % scale.denominator); |
| 225 RTC_DCHECK_EQ(0, *cropped_height % scale.denominator); |
| 226 |
| 227 // Calculate final output size. |
| 228 *out_width = *cropped_width / scale.denominator * scale.numerator; |
| 229 *out_height = *cropped_height / scale.denominator * scale.numerator; |
184 | 230 |
185 ++frames_out_; | 231 ++frames_out_; |
186 if (scale != 1) | 232 if (scale.numerator != scale.denominator) |
187 ++frames_scaled_; | 233 ++frames_scaled_; |
188 | 234 |
189 if (previous_width_ && (previous_width_ != output_width || | 235 if (previous_width_ && (previous_width_ != *out_width || |
190 previous_height_ != output_height)) { | 236 previous_height_ != *out_height)) { |
191 ++adaption_changes_; | 237 ++adaption_changes_; |
192 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out " | 238 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out " |
193 << frames_out_ << " / in " << frames_in_ | 239 << frames_out_ << " / in " << frames_in_ |
194 << " Changes: " << adaption_changes_ << " Input: " << in_width | 240 << " Changes: " << adaption_changes_ << " Input: " << in_width |
195 << "x" << in_height << " i" << input_format_.interval | 241 << "x" << in_height << " i" << input_interval_ |
196 << " Scale: " << scale << " Output: " << output_width << "x" | 242 << " Scale: " << scale.numerator << "/" << scale.denominator |
197 << output_height << " i" << output_format_.interval; | 243 << " Output: " << *out_width << "x" << *out_height << " i" |
| 244 << (requested_format_ ? requested_format_->interval : 0); |
198 } | 245 } |
199 | 246 |
200 output_format_.width = output_width; | 247 previous_width_ = *out_width; |
201 output_format_.height = output_height; | 248 previous_height_ = *out_height; |
202 previous_width_ = output_width; | |
203 previous_height_ = output_height; | |
204 | |
205 return output_format_; | |
206 } | 249 } |
207 | 250 |
208 void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { | 251 void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { |
209 rtc::CritScope cs(&critical_section_); | 252 rtc::CritScope cs(&critical_section_); |
210 format_request_max_pixel_count_ = format.width * format.height; | 253 requested_format_ = rtc::Optional<VideoFormat>(format); |
211 output_format_.interval = format.interval; | 254 interval_next_frame_ = 0; |
212 Adapt(std::min(format_request_max_pixel_count_, | |
213 resolution_request_max_pixel_count_), | |
214 0); | |
215 } | 255 } |
216 | 256 |
217 void VideoAdapter::OnResolutionRequest( | 257 void VideoAdapter::OnResolutionRequest( |
218 rtc::Optional<int> max_pixel_count, | 258 rtc::Optional<int> max_pixel_count, |
219 rtc::Optional<int> max_pixel_count_step_up) { | 259 rtc::Optional<int> max_pixel_count_step_up) { |
220 rtc::CritScope cs(&critical_section_); | 260 rtc::CritScope cs(&critical_section_); |
221 resolution_request_max_pixel_count_ = | 261 resolution_request_max_pixel_count_ = |
222 max_pixel_count.value_or(std::numeric_limits<int>::max()); | 262 max_pixel_count.value_or(std::numeric_limits<int>::max()); |
223 Adapt(std::min(format_request_max_pixel_count_, | 263 resolution_request_max_pixel_count_step_up_ = |
224 resolution_request_max_pixel_count_), | 264 max_pixel_count_step_up.value_or(0); |
225 max_pixel_count_step_up.value_or(0)); | |
226 } | |
227 | |
228 bool VideoAdapter::Adapt(int max_num_pixels, int max_pixel_count_step_up) { | |
229 float scale_lower = | |
230 FindScaleLessThanOrEqual(input_format_.width, input_format_.height, | |
231 max_num_pixels, &max_num_pixels); | |
232 float scale_upper = | |
233 max_pixel_count_step_up > 0 | |
234 ? FindScaleLargerThan(input_format_.width, input_format_.height, | |
235 max_pixel_count_step_up, | |
236 &max_pixel_count_step_up) | |
237 : 1.f; | |
238 | |
239 bool use_max_pixel_count_step_up = | |
240 max_pixel_count_step_up > 0 && max_num_pixels > max_pixel_count_step_up; | |
241 | |
242 int old_num_pixels = output_num_pixels_; | |
243 output_num_pixels_ = | |
244 use_max_pixel_count_step_up ? max_pixel_count_step_up : max_num_pixels; | |
245 // Log the new size. | |
246 float scale = use_max_pixel_count_step_up ? scale_upper : scale_lower; | |
247 int new_width = static_cast<int>(input_format_.width * scale + .5f); | |
248 int new_height = static_cast<int>(input_format_.height * scale + .5f); | |
249 | |
250 bool changed = output_num_pixels_ != old_num_pixels; | |
251 LOG(LS_INFO) << "OnResolutionRequest: " | |
252 << " Max pixels: " << max_num_pixels | |
253 << " Max pixels step up: " << max_pixel_count_step_up | |
254 << " Output Pixels: " << output_num_pixels_ | |
255 << " Input: " << input_format_.width << "x" | |
256 << input_format_.height << " Scale: " << scale | |
257 << " Resolution: " << new_width << "x" << new_height | |
258 << " Changed: " << (changed ? "true" : "false"); | |
259 | |
260 return changed; | |
261 } | 265 } |
262 | 266 |
263 } // namespace cricket | 267 } // namespace cricket |
OLD | NEW |