Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: webrtc/media/base/videoadapter.cc

Issue 1966273002: VideoAdapter: Add cropping based on OnOutputFormatRequest() (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Remove unused variable Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/media/base/videoadapter.h ('k') | webrtc/media/base/videoadapter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
OLDNEW
« no previous file with comments | « webrtc/media/base/videoadapter.h ('k') | webrtc/media/base/videoadapter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698