blob: 50e00f7680676c10e9747bbf8750b55a6ff5f82d [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/media/capture/web_contents_auto_scaler.h"
#include <memory>
#include "base/test/scoped_feature_list.h"
#include "media/base/media_switches.h"
#include "media/capture/video/video_capture_feedback.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
namespace content {
namespace {
// Standardized screen resolutions to test common scenarios.
constexpr gfx::Size kSize720p{1280, 720};
constexpr gfx::Size kSize1080p{1920, 1080};
class FakeDelegate : public WebContentsAutoScaler::Delegate {
public:
FakeDelegate() = default;
~FakeDelegate() override = default;
void SetCaptureScaleOverride(float scale) override {
scale_override_ = scale;
}
float GetCaptureScaleOverride() const override { return scale_override_; }
private:
float scale_override_ = 1.0f;
};
class WebContentsAutoScalerTest : public ::testing::Test {
protected:
WebContentsAutoScalerTest() = default;
~WebContentsAutoScalerTest() override = default;
void CreateAutoScaler(const gfx::Size& capture_size) {
scaler_ = std::make_unique<WebContentsAutoScaler>(delegate_, capture_size);
}
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<WebContentsAutoScaler> scaler_;
FakeDelegate delegate_;
};
TEST_F(WebContentsAutoScalerTest, SetsScaleOverride) {
CreateAutoScaler(kSize1080p);
// Capture starts at 1080p size, there should be no scale override.
EXPECT_EQ(delegate_.GetCaptureScaleOverride(), 1.0f);
// Adjust the captured content size to a smaller size. This should activate a
// scale override correlative to the difference between the two resolutions.
scaler_->SetCapturedContentSize(kSize720p);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5);
// Scaling should go up to a maximum of 2.0.
scaler_->SetCapturedContentSize(gfx::Size(960, 540));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
// The tracker should assume that we are now already scaled by the override
// value, and so shouldn't change the override if we start getting frames that
// are large enough.
scaler_->SetCapturedContentSize(gfx::Size(1920, 1080));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
// If a frame ends up being larger than the capture_size, the scale
// should get adjusted downwards so that the post-scaling size matches
// the capture size. This assumes a current scale override of 2.0f.
scaler_->SetCapturedContentSize(gfx::Size(2560, 1440));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
// The scaled size should now match the capture size with a scale
// override of 1.5.
scaler_->SetCapturedContentSize(gfx::Size(1920, 1080));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
// The scaling calculation is based on fitting a scaled copy of the
// source rectangle within the capture region, preserving aspect ratio.
// If the content size changes in a way that doesn't affect the scale
// factor (i.e. letterboxing or pillarboxing), the scale override remains
// unchanged.
scaler_->SetCapturedContentSize(gfx::Size(1080, 1080));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
scaler_->SetCapturedContentSize(gfx::Size(1920, 540));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
}
TEST_F(WebContentsAutoScalerTest, SettingScaleFactorMaintainsStableCapture) {
CreateAutoScaler(kSize1080p);
// Adjust the captured content size to a smaller size. This should activate a
// scale override correlative to the difference between the two resolutions.
scaler_->SetCapturedContentSize(kSize720p);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5);
// It should now be scaled to the capture_size, meaning 1080P. The
// scale override factor should be unaffected.
scaler_->SetCapturedContentSize(kSize1080p);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5);
}
TEST_F(WebContentsAutoScalerTest, HighDpiIsRoundedIfBetweenBounds) {
CreateAutoScaler(kSize1080p);
// Both factors should be 1.4f, which should be between bounds and rounded
// up.
scaler_->SetCapturedContentSize(gfx::Size{1370, 771});
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5);
}
TEST_F(WebContentsAutoScalerTest, HighDpiIsRoundedIfBetweenDifferentBounds) {
CreateAutoScaler(kSize1080p);
// Both factors should be 1.6f, which should be between bounds and rounded
// down.
scaler_->SetCapturedContentSize(gfx::Size{1200, 675});
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
}
TEST_F(WebContentsAutoScalerTest, HighDpiIsRoundedToMinimum) {
CreateAutoScaler(kSize1080p);
// Both factors should be 1.3f, which should be between bounds and rounded
// down.
scaler_->SetCapturedContentSize(gfx::Size{1477, 831});
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
}
TEST_F(WebContentsAutoScalerTest, HighDpiIsRoundedToMaximum) {
CreateAutoScaler(kSize1080p);
// Both factors should be well over the maximum of 2.0f.
scaler_->SetCapturedContentSize(gfx::Size{320, 240});
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
}
TEST_F(WebContentsAutoScalerTest, HighDpiScalingIsStable) {
CreateAutoScaler(kSize1080p);
// Both factors should be 1.25f, which should be exactly a scaling factor.
static constexpr gfx::Size kContentSize(1536, 864);
scaler_->SetCapturedContentSize(kContentSize);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
// Now that its applied, it should stay the same.
static const gfx::Size kScaledContentSize =
gfx::ScaleToRoundedSize(kContentSize, 1.25f);
scaler_->SetCapturedContentSize(kScaledContentSize);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
// If it varies slightly that shouldn't result in any changes.
static const gfx::Size kScaledLargerContentSize =
gfx::ScaleToRoundedSize(kContentSize, 1.27f);
scaler_->SetCapturedContentSize(kScaledLargerContentSize);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
static const gfx::Size kScaledSmallerContentSize =
gfx::ScaleToRoundedSize(kContentSize, 1.23f);
scaler_->SetCapturedContentSize(kScaledSmallerContentSize);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
}
TEST_F(WebContentsAutoScalerTest, HighDpiAdjustsForResourceUtilization) {
CreateAutoScaler(kSize1080p);
// Both factors should be 2.0f, which should be exactly a scaling factor.
scaler_->SetCapturedContentSize(gfx::Size(960, 540));
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
// Start with default feedback, which should be ignored.
media::VideoCaptureFeedback feedback;
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
// As the feedback continues to be poor, the scale override should lower.
feedback.resource_utilization = 0.9f;
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.75f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.0f);
// If things get significantly better, it should go back up.
feedback.resource_utilization = 0.49f;
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.75f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 2.0f);
}
TEST_F(WebContentsAutoScalerTest, HighDpiAdjustsForMaxPixelRate) {
CreateAutoScaler(kSize1080p);
scaler_->SetCapturedContentSize(kSize720p);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
// Test using too many pixels.
media::VideoCaptureFeedback feedback;
feedback.max_pixels = kSize720p.width() * kSize720p.height() - 1;
// We should lower the maximum, which should eventually lower the override.
// First, max is now 1.75f.
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
// Now max is 1.5f.
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
// Now max is 1.25f.
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
// Now max is 1.0f.
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.0f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.0f);
// Things should only change if it gets significantly better.
feedback.max_pixels = kSize720p.width() * kSize720p.height() + 1;
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.0f);
feedback.max_pixels = kSize720p.width() * kSize720p.height() * 1.33f;
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.25f);
scaler_->OnUtilizationReport(feedback);
EXPECT_DOUBLE_EQ(delegate_.GetCaptureScaleOverride(), 1.5f);
}
} // namespace
} // namespace content