Chromium Code Reviews| 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 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 // Must be sorted in decreasing scale factors for FindScaleLargerThan to work. | 23 // Must be sorted in decreasing scale factors for FindScaleLargerThan to work. |
| 24 const float kScaleFactors[] = { | 24 const float kScaleFactors[] = { |
| 25 1.f / 1.f, // Full size. | 25 1.f / 1.f, // Full size. |
| 26 3.f / 4.f, // 3/4 scale. | 26 3.f / 4.f, // 3/4 scale. |
| 27 1.f / 2.f, // 1/2 scale. | 27 1.f / 2.f, // 1/2 scale. |
| 28 3.f / 8.f, // 3/8 scale. | 28 3.f / 8.f, // 3/8 scale. |
| 29 1.f / 4.f, // 1/4 scale. | 29 1.f / 4.f, // 1/4 scale. |
| 30 3.f / 16.f, // 3/16 scale. | 30 3.f / 16.f, // 3/16 scale. |
| 31 }; | 31 }; |
| 32 | 32 |
| 33 float FindScaleLessThanOrEqual(int width, | 33 float FindScaleLessThanOrEqual(int input_num_pixels, |
| 34 int height, | |
| 35 int target_num_pixels, | 34 int target_num_pixels, |
| 36 int* resulting_number_of_pixels) { | 35 int* resulting_number_of_pixels) { |
| 37 float best_distance = std::numeric_limits<float>::max(); | 36 float best_distance = std::numeric_limits<float>::max(); |
| 38 float best_scale = 0.0f; // Default to 0 if nothing matches. | 37 float best_scale = 0.0f; // Default to 0 if nothing matches. |
| 39 float pixels = width * height; | |
| 40 float best_number_of_pixels = 0.0f; | 38 float best_number_of_pixels = 0.0f; |
| 41 for (const auto& scale : kScaleFactors) { | 39 for (const auto& scale : kScaleFactors) { |
| 42 float test_num_pixels = pixels * scale * scale; | 40 float test_num_pixels = input_num_pixels * scale * scale; |
| 43 float diff = target_num_pixels - test_num_pixels; | 41 float diff = target_num_pixels - test_num_pixels; |
| 44 if (diff < 0) { | 42 if (diff < 0) { |
| 45 continue; | 43 continue; |
| 46 } | 44 } |
| 47 if (diff < best_distance) { | 45 if (diff < best_distance) { |
| 48 best_distance = diff; | 46 best_distance = diff; |
| 49 best_scale = scale; | 47 best_scale = scale; |
| 50 best_number_of_pixels = test_num_pixels; | 48 best_number_of_pixels = test_num_pixels; |
| 51 if (best_distance == 0) { // Found exact match. | 49 if (best_distance == 0) { // Found exact match. |
| 52 break; | 50 break; |
| 53 } | 51 } |
| 54 } | 52 } |
| 55 } | 53 } |
| 56 if (resulting_number_of_pixels) { | 54 if (resulting_number_of_pixels) { |
| 57 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); | 55 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); |
| 58 } | 56 } |
| 59 return best_scale; | 57 return best_scale; |
| 60 } | 58 } |
| 61 | 59 |
| 62 float FindScaleLargerThan(int width, | 60 float FindScaleLargerThan(int input_num_pixels, |
| 63 int height, | |
| 64 int target_num_pixels, | 61 int target_num_pixels, |
| 65 int* resulting_number_of_pixels) { | 62 int* resulting_number_of_pixels) { |
| 66 float best_distance = std::numeric_limits<float>::max(); | 63 float best_distance = std::numeric_limits<float>::max(); |
| 67 float best_scale = 1.f; // Default to unscaled if nothing matches. | 64 float best_scale = 1.f; // Default to unscaled if nothing matches. |
| 68 float pixels = width * height; | 65 // Default to input number of pixels. |
| 69 float best_number_of_pixels = pixels; // Default to input number of pixels. | 66 float best_number_of_pixels = input_num_pixels; |
| 70 for (const auto& scale : kScaleFactors) { | 67 for (const auto& scale : kScaleFactors) { |
| 71 float test_num_pixels = pixels * scale * scale; | 68 float test_num_pixels = input_num_pixels * scale * scale; |
| 72 float diff = test_num_pixels - target_num_pixels; | 69 float diff = test_num_pixels - target_num_pixels; |
| 73 if (diff <= 0) { | 70 if (diff <= 0) { |
| 74 break; | 71 break; |
| 75 } | 72 } |
| 76 if (diff < best_distance) { | 73 if (diff < best_distance) { |
| 77 best_distance = diff; | 74 best_distance = diff; |
| 78 best_scale = scale; | 75 best_scale = scale; |
| 79 best_number_of_pixels = test_num_pixels; | 76 best_number_of_pixels = test_num_pixels; |
| 80 } | 77 } |
| 81 } | 78 } |
| 82 | 79 |
| 83 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); | 80 *resulting_number_of_pixels = static_cast<int>(best_number_of_pixels + .5f); |
| 84 return best_scale; | 81 return best_scale; |
| 85 } | 82 } |
| 86 | 83 |
| 84 float FindScale(int input_num_pixels, | |
| 85 int max_pixel_count_step_up, | |
| 86 int max_pixel_count) { | |
| 87 // Try scale just above |max_pixel_count_step_up_|. | |
| 88 if (max_pixel_count_step_up > 0) { | |
| 89 int resulting_pixel_count; | |
| 90 const float scale = FindScaleLargerThan( | |
| 91 input_num_pixels, max_pixel_count_step_up, &resulting_pixel_count); | |
| 92 if (resulting_pixel_count <= max_pixel_count) | |
| 93 return scale; | |
| 94 } | |
| 95 // Return largest scale below |max_pixel_count|. | |
| 96 return FindScaleLessThanOrEqual(input_num_pixels, max_pixel_count, nullptr); | |
| 97 } | |
| 98 | |
| 87 } // namespace | 99 } // namespace |
| 88 | 100 |
| 89 namespace cricket { | 101 namespace cricket { |
| 90 | 102 |
| 91 VideoAdapter::VideoAdapter() | 103 VideoAdapter::VideoAdapter() |
| 92 : output_num_pixels_(std::numeric_limits<int>::max()), | 104 : frames_in_(0), |
| 93 frames_in_(0), | |
| 94 frames_out_(0), | 105 frames_out_(0), |
| 95 frames_scaled_(0), | 106 frames_scaled_(0), |
| 96 adaption_changes_(0), | 107 adaption_changes_(0), |
| 97 previous_width_(0), | 108 previous_width_(0), |
| 98 previous_height_(0), | 109 previous_height_(0), |
| 110 input_interval_(0), | |
| 99 interval_next_frame_(0), | 111 interval_next_frame_(0), |
| 100 format_request_max_pixel_count_(std::numeric_limits<int>::max()), | 112 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()), |
| 101 resolution_request_max_pixel_count_(std::numeric_limits<int>::max()) {} | 113 resolution_request_max_pixel_count_step_up_(0) {} |
| 102 | 114 |
| 103 VideoAdapter::~VideoAdapter() {} | 115 VideoAdapter::~VideoAdapter() {} |
| 104 | 116 |
| 105 void VideoAdapter::SetExpectedInputFrameInterval(int64_t interval) { | 117 void VideoAdapter::SetExpectedInputFrameInterval(int64_t interval) { |
| 106 // TODO(perkj): Consider measuring input frame rate instead. | 118 // TODO(perkj): Consider measuring input frame rate instead. |
| 107 // Frame rate typically varies depending on lighting. | 119 // Frame rate typically varies depending on lighting. |
| 108 rtc::CritScope cs(&critical_section_); | 120 rtc::CritScope cs(&critical_section_); |
| 109 input_format_.interval = interval; | 121 input_interval_ = interval; |
| 110 } | 122 } |
| 111 | 123 |
| 112 void VideoAdapter::SetInputFormat(const VideoFormat& format) { | 124 void VideoAdapter::AdaptFrameResolution(int in_width, |
| 113 bool is_resolution_change = (input_format().width != format.width || | 125 int in_height, |
| 114 input_format().height != format.height); | 126 int* cropped_width, |
| 115 int64_t old_input_interval = input_format_.interval; | 127 int* cropped_height, |
| 116 input_format_ = format; | 128 int* out_width, |
| 117 output_format_.interval = | 129 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_); | 130 rtc::CritScope cs(&critical_section_); |
| 140 ++frames_in_; | 131 ++frames_in_; |
| 141 | 132 |
| 142 SetInputFormat(VideoFormat( | 133 // The max output pixel count is the minimum of the requests from |
| 143 in_width, in_height, input_format_.interval, input_format_.fourcc)); | 134 // OnOutputFormatRequest and OnResolutionRequest. |
| 135 int max_pixel_count = resolution_request_max_pixel_count_; | |
| 136 if (requested_format_) { | |
| 137 max_pixel_count = std::min( | |
| 138 max_pixel_count, requested_format_->width * requested_format_->height); | |
| 139 } | |
| 144 | 140 |
| 145 // Drop the input frame if necessary. | 141 // Drop the input frame if necessary. |
| 146 bool should_drop = false; | 142 bool should_drop = false; |
| 147 if (!output_num_pixels_) { | 143 if (max_pixel_count == 0) { |
| 148 // Drop all frames as the output format is 0x0. | 144 // Drop all frames as the output format is 0x0. |
| 149 should_drop = true; | 145 should_drop = true; |
| 150 } else { | 146 } else if (requested_format_ && requested_format_->interval > 0) { |
| 151 // Drop some frames based on input fps and output fps. | 147 // Drop some frames based on input fps and output fps. |
| 152 // Normally output fps is less than input fps. | 148 // Normally output fps is less than input fps. |
| 153 interval_next_frame_ += input_format_.interval; | 149 interval_next_frame_ += input_interval_; |
| 154 if (output_format_.interval > 0) { | 150 if (interval_next_frame_ >= requested_format_->interval) { |
| 155 if (interval_next_frame_ >= output_format_.interval) { | 151 interval_next_frame_ %= requested_format_->interval; |
|
nisse-webrtc
2016/05/13 07:58:19
If it is ensured that input_interval < requested_f
magjed_webrtc
2016/05/13 09:12:59
1) I think in the near future, we want to remove t
nisse-webrtc
2016/05/13 11:47:06
I see. Don't bother changing that now, then.
magjed_webrtc
2016/05/13 14:22:28
Done.
| |
| 156 interval_next_frame_ %= output_format_.interval; | 152 } else { |
| 157 } else { | 153 should_drop = true; |
| 158 should_drop = true; | |
| 159 } | |
| 160 } | 154 } |
| 161 } | 155 } |
| 162 if (should_drop) { | 156 if (should_drop) { |
| 163 // Show VAdapt log every 90 frames dropped. (3 seconds) | 157 // Show VAdapt log every 90 frames dropped. (3 seconds) |
| 164 if ((frames_in_ - frames_out_) % 90 == 0) { | 158 if ((frames_in_ - frames_out_) % 90 == 0) { |
| 165 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed | 159 // TODO(fbarchard): Reduce to LS_VERBOSE when adapter info is not needed |
| 166 // in default calls. | 160 // in default calls. |
| 167 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ | 161 LOG(LS_INFO) << "VAdapt Drop Frame: scaled " << frames_scaled_ |
| 168 << " / out " << frames_out_ | 162 << " / out " << frames_out_ |
| 169 << " / in " << frames_in_ | 163 << " / in " << frames_in_ |
| 170 << " Changes: " << adaption_changes_ | 164 << " Changes: " << adaption_changes_ |
| 171 << " Input: " << in_width | 165 << " Input: " << in_width |
| 172 << "x" << in_height | 166 << "x" << in_height |
| 173 << " i" << input_format_.interval | 167 << " i" << input_interval_ |
| 174 << " Output: i" << output_format_.interval; | 168 << " Output: i" |
| 169 << (requested_format_ ? requested_format_->interval : 0); | |
| 175 } | 170 } |
| 176 | 171 |
| 177 return VideoFormat(); // Drop frame. | 172 // Drop frame. |
| 173 *cropped_width = 0; | |
| 174 *cropped_height = 0; | |
| 175 *out_width = 0; | |
| 176 *out_height = 0; | |
| 177 return; | |
| 178 } | 178 } |
| 179 | 179 |
| 180 const float scale = FindScaleLessThanOrEqual(in_width, in_height, | 180 // Calculate how the input should be cropped. |
| 181 output_num_pixels_, nullptr); | 181 if (!requested_format_ || |
| 182 const int output_width = static_cast<int>(in_width * scale + .5f); | 182 requested_format_->width == 0 || requested_format_->height == 0) { |
| 183 const int output_height = static_cast<int>(in_height * scale + .5f); | 183 *cropped_width = in_width; |
| 184 *cropped_height = in_height; | |
| 185 } else { | |
| 186 // Adjust |requested_format_| orientation to match input. | |
| 187 if ((in_width > in_height) != | |
| 188 (requested_format_->width > requested_format_->height)) { | |
| 189 std::swap(requested_format_->width, requested_format_->height); | |
| 190 } | |
| 191 const float requested_aspect = | |
|
nisse-webrtc
2016/05/13 07:58:19
I liked my integer-only crop logic in
https://co
magjed_webrtc
2016/05/13 09:12:59
Your interger-only crop is clever, but I don't see
nisse-webrtc
2016/05/13 11:47:06
Ok.
| |
| 192 requested_format_->width / | |
| 193 static_cast<float>(requested_format_->height); | |
| 194 *cropped_width = | |
| 195 std::min(in_width, static_cast<int>(in_height * requested_aspect)); | |
| 196 *cropped_height = | |
| 197 std::min(in_height, static_cast<int>(in_width / requested_aspect)); | |
| 198 } | |
| 199 | |
| 200 // Find best scale factor. | |
| 201 const float scale = | |
| 202 FindScale(*cropped_width * *cropped_height, | |
| 203 resolution_request_max_pixel_count_step_up_, max_pixel_count); | |
| 204 | |
| 205 // Calculate final output size. | |
| 206 *out_width = static_cast<int>(*cropped_width * scale + .5f); | |
|
nisse-webrtc
2016/05/13 07:58:19
But now I'm getting confused... I thought the requ
magjed_webrtc
2016/05/13 09:12:59
I need to base the scale factor on the number of c
| |
| 207 *out_height = static_cast<int>(*cropped_height * scale + .5f); | |
| 184 | 208 |
| 185 ++frames_out_; | 209 ++frames_out_; |
| 186 if (scale != 1) | 210 if (scale != 1) |
| 187 ++frames_scaled_; | 211 ++frames_scaled_; |
| 188 | 212 |
| 189 if (previous_width_ && (previous_width_ != output_width || | 213 if (previous_width_ && (previous_width_ != *out_width || |
| 190 previous_height_ != output_height)) { | 214 previous_height_ != *out_height)) { |
| 191 ++adaption_changes_; | 215 ++adaption_changes_; |
| 192 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out " | 216 LOG(LS_INFO) << "Frame size changed: scaled " << frames_scaled_ << " / out " |
| 193 << frames_out_ << " / in " << frames_in_ | 217 << frames_out_ << " / in " << frames_in_ |
| 194 << " Changes: " << adaption_changes_ << " Input: " << in_width | 218 << " Changes: " << adaption_changes_ << " Input: " << in_width |
| 195 << "x" << in_height << " i" << input_format_.interval | 219 << "x" << in_height << " i" << input_interval_ |
| 196 << " Scale: " << scale << " Output: " << output_width << "x" | 220 << " Scale: " << scale << " Output: " << *out_width << "x" |
| 197 << output_height << " i" << output_format_.interval; | 221 << *out_height << " i" |
| 222 << (requested_format_ ? requested_format_->interval : 0); | |
| 198 } | 223 } |
| 199 | 224 |
| 200 output_format_.width = output_width; | 225 previous_width_ = *out_width; |
| 201 output_format_.height = output_height; | 226 previous_height_ = *out_height; |
| 202 previous_width_ = output_width; | |
| 203 previous_height_ = output_height; | |
| 204 | |
| 205 return output_format_; | |
| 206 } | 227 } |
| 207 | 228 |
| 208 void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { | 229 void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { |
| 209 rtc::CritScope cs(&critical_section_); | 230 rtc::CritScope cs(&critical_section_); |
| 210 format_request_max_pixel_count_ = format.width * format.height; | 231 requested_format_ = rtc::Optional<VideoFormat>(format); |
| 211 output_format_.interval = format.interval; | |
| 212 Adapt(std::min(format_request_max_pixel_count_, | |
| 213 resolution_request_max_pixel_count_), | |
| 214 0); | |
| 215 } | 232 } |
| 216 | 233 |
| 217 void VideoAdapter::OnResolutionRequest( | 234 void VideoAdapter::OnResolutionRequest( |
| 218 rtc::Optional<int> max_pixel_count, | 235 rtc::Optional<int> max_pixel_count, |
| 219 rtc::Optional<int> max_pixel_count_step_up) { | 236 rtc::Optional<int> max_pixel_count_step_up) { |
| 220 rtc::CritScope cs(&critical_section_); | 237 rtc::CritScope cs(&critical_section_); |
| 221 resolution_request_max_pixel_count_ = | 238 resolution_request_max_pixel_count_ = |
| 222 max_pixel_count.value_or(std::numeric_limits<int>::max()); | 239 max_pixel_count.value_or(std::numeric_limits<int>::max()); |
| 223 Adapt(std::min(format_request_max_pixel_count_, | 240 resolution_request_max_pixel_count_step_up_ = |
| 224 resolution_request_max_pixel_count_), | 241 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 } | 242 } |
| 262 | 243 |
| 263 } // namespace cricket | 244 } // namespace cricket |
| OLD | NEW |