Index: webrtc/media/base/videoadapter.cc |
diff --git a/webrtc/media/base/videoadapter.cc b/webrtc/media/base/videoadapter.cc |
index 8f7288dc590290fe40213b998b6a383b5a3147bc..660df8ad673e8f40668d8f150722cb21082073ee 100644 |
--- a/webrtc/media/base/videoadapter.cc |
+++ b/webrtc/media/base/videoadapter.cc |
@@ -26,6 +26,12 @@ namespace { |
struct Fraction { |
int numerator; |
int denominator; |
+ |
+ // Determines number of output pixels if both width and height of an input of |
+ // |input_pixels| pixels is scaled with the fraction numerator / denominator. |
+ int scale_pixel_count(int input_pixels) { |
+ return (numerator * numerator * input_pixels) / (denominator * denominator); |
+ } |
}; |
// Round |value_to_round| to a multiple of |multiple|. Prefer rounding upwards, |
@@ -37,29 +43,54 @@ int roundUp(int value_to_round, int multiple, int max_value) { |
: (max_value / multiple * multiple); |
} |
-// Generates a scale factor that makes |input_num_pixels| smaller or |
-// larger than |target_num_pixels|, depending on the value of |step_up|. |
-Fraction FindScale(int input_num_pixels, int target_num_pixels, bool step_up) { |
+// Generates a scale factor that makes |input_pixels| close to |target_pixels|, |
+// but no higher than |max_pixels|. |
+Fraction FindScale(int input_pixels, int target_pixels, int max_pixels) { |
// This function only makes sense for a positive target. |
- RTC_DCHECK_GT(target_num_pixels, 0); |
+ RTC_DCHECK_GT(target_pixels, 0); |
+ RTC_DCHECK_GT(max_pixels, 0); |
+ RTC_DCHECK_GE(max_pixels, target_pixels); |
+ |
+ // Don't scale up original. |
+ if (target_pixels >= input_pixels) |
+ return Fraction{1, 1}; |
+ |
+ Fraction current_scale = Fraction{1, 1}; |
Fraction best_scale = Fraction{1, 1}; |
- Fraction last_scale = Fraction{1, 1}; |
- const float target_scale = |
- sqrt(target_num_pixels / static_cast<float>(input_num_pixels)); |
- while (best_scale.numerator > (target_scale * best_scale.denominator)) { |
- last_scale = best_scale; |
- if (best_scale.numerator % 3 == 0 && best_scale.denominator % 2 == 0) { |
- // Multiply by 2/3 |
- best_scale.numerator /= 3; |
- best_scale.denominator /= 2; |
+ // The minimum (absolute) difference between the number of output pixels and |
+ // the target pixel count. |
+ int min_pixel_diff = std::numeric_limits<int>::max(); |
+ if (input_pixels < max_pixels) { |
+ // Start condition for 1/1 case, if it is less than max. |
+ min_pixel_diff = std::abs(input_pixels - target_pixels); |
+ } |
+ |
+ // Alternately scale down by 2/3 and 3/4. This results in fractions which are |
+ // effectively scalable. For instance, starting at 1280x720 will result in |
+ // the series (3/4) => 960x540, (1/2) => 640x360, (3/8) => 480x270, |
+ // (1/4) => 320x180, (3/16) => 240x125, (1/8) => 160x90. |
+ while (current_scale.scale_pixel_count(input_pixels) > target_pixels) { |
+ if (current_scale.numerator % 3 == 0 && |
+ current_scale.denominator % 2 == 0) { |
+ // Multiply by 2/3. |
+ current_scale.numerator /= 3; |
+ current_scale.denominator /= 2; |
} else { |
- // Multiply by 3/4 |
- best_scale.numerator *= 3; |
- best_scale.denominator *= 4; |
+ // Multiply by 3/4. |
+ current_scale.numerator *= 3; |
+ current_scale.denominator *= 4; |
+ } |
+ |
+ int output_pixels = current_scale.scale_pixel_count(input_pixels); |
+ if (output_pixels <= max_pixels) { |
+ int diff = std::abs(target_pixels - output_pixels); |
+ if (diff < min_pixel_diff) { |
+ min_pixel_diff = diff; |
+ best_scale = current_scale; |
+ } |
} |
} |
- if (step_up) |
- return last_scale; |
+ |
return best_scale; |
} |
} // namespace |
@@ -74,8 +105,8 @@ VideoAdapter::VideoAdapter(int required_resolution_alignment) |
previous_width_(0), |
previous_height_(0), |
required_resolution_alignment_(required_resolution_alignment), |
- resolution_request_max_pixel_count_(std::numeric_limits<int>::max()), |
- step_up_(false) {} |
+ resolution_request_target_pixel_count_(std::numeric_limits<int>::max()), |
+ resolution_request_max_pixel_count_(std::numeric_limits<int>::max()) {} |
VideoAdapter::VideoAdapter() : VideoAdapter(1) {} |
@@ -124,14 +155,11 @@ bool VideoAdapter::AdaptFrameResolution(int in_width, |
// OnOutputFormatRequest and OnResolutionRequest. |
int max_pixel_count = resolution_request_max_pixel_count_; |
if (requested_format_) { |
- // TODO(kthelgason): remove the - |step_up_| hack when we change how |
- // resolution is requested from VideoSourceProxy. |
- // This is required because we must not scale above the requested |
- // format so we subtract one when scaling up. |
max_pixel_count = std::min( |
- max_pixel_count, requested_format_->width * requested_format_->height - |
- static_cast<int>(step_up_)); |
+ max_pixel_count, requested_format_->width * requested_format_->height); |
} |
+ int target_pixel_count = |
+ std::min(resolution_request_target_pixel_count_, max_pixel_count); |
// Drop the input frame if necessary. |
if (max_pixel_count <= 0 || !KeepFrame(in_timestamp_ns)) { |
@@ -173,8 +201,8 @@ bool VideoAdapter::AdaptFrameResolution(int in_width, |
*cropped_height = |
std::min(in_height, static_cast<int>(in_width / requested_aspect)); |
} |
- const Fraction scale = |
- FindScale(*cropped_width * *cropped_height, max_pixel_count, step_up_); |
+ const Fraction scale = FindScale((*cropped_width) * (*cropped_height), |
+ target_pixel_count, max_pixel_count); |
// Adjust cropping slightly to get even integer output size and a perfect |
// scale factor. Make sure the resulting dimensions are aligned correctly |
// to be nice to hardware encoders. |
@@ -222,12 +250,13 @@ void VideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { |
} |
void VideoAdapter::OnResolutionRequest( |
- rtc::Optional<int> max_pixel_count, |
- rtc::Optional<int> max_pixel_count_step_up) { |
+ const rtc::Optional<int>& target_pixel_count, |
+ const rtc::Optional<int>& max_pixel_count) { |
rtc::CritScope cs(&critical_section_); |
- resolution_request_max_pixel_count_ = max_pixel_count.value_or( |
- max_pixel_count_step_up.value_or(std::numeric_limits<int>::max())); |
- step_up_ = static_cast<bool>(max_pixel_count_step_up); |
+ resolution_request_max_pixel_count_ = |
+ max_pixel_count.value_or(std::numeric_limits<int>::max()); |
+ resolution_request_target_pixel_count_ = |
+ target_pixel_count.value_or(resolution_request_max_pixel_count_); |
} |
} // namespace cricket |