Move usage of QualityScaler to ViEEncoder.
This brings QualityScaler much more in line with OveruseFrameDetector. The two classes are conceptually similar, and should be used in the same way. The biggest changes in this CL are: - Quality scaling is now only done in ViEEncoder and not in each encoder implementation separately. - QualityScaler now checks the average QP asynchronously, instead of having to be polled on each frame. - QualityScaler is no longer responsible for actually scaling the frames, but has a callback to ViEEncoder that it uses to express it's desire for lower resolution. BUG=webrtc:6495 Review-Url: https://codereview.webrtc.org/2398963003 Cr-Commit-Position: refs/heads/master@{#15286}
This commit is contained in:
@ -10,28 +10,49 @@
|
||||
|
||||
#include "webrtc/modules/video_coding/utility/quality_scaler.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/base/event.h"
|
||||
#include "webrtc/base/task_queue.h"
|
||||
#include "webrtc/test/gmock.h"
|
||||
#include "webrtc/test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
static const int kNumSeconds = 10;
|
||||
static const int kWidth = 1920;
|
||||
static const int kHeight = 1080;
|
||||
static const int kFramerate = 30;
|
||||
static const int kLowQp = 15;
|
||||
static const int kNormalQp = 30;
|
||||
static const int kLowQpThreshold = 18;
|
||||
static const int kHighQp = 40;
|
||||
static const int kDisabledBadQpThreshold = 64;
|
||||
static const int kLowInitialBitrateKbps = 300;
|
||||
// These values need to be in sync with corresponding constants
|
||||
// in quality_scaler.cc
|
||||
static const int kMeasureSecondsFastUpscale = 2;
|
||||
static const int kMeasureSecondsUpscale = 5;
|
||||
static const int kMeasureSecondsDownscale = 5;
|
||||
static const int kMinDownscaleDimension = 140;
|
||||
static const size_t kDefaultTimeoutMs = 1000;
|
||||
} // namespace
|
||||
|
||||
class MockScaleObserver : public ScalingObserverInterface {
|
||||
public:
|
||||
MockScaleObserver() : event(false, false) {}
|
||||
virtual ~MockScaleObserver() {}
|
||||
|
||||
void ScaleUp(ScaleReason r) override {
|
||||
scaled_up++;
|
||||
event.Set();
|
||||
}
|
||||
void ScaleDown(ScaleReason r) override {
|
||||
scaled_down++;
|
||||
event.Set();
|
||||
}
|
||||
|
||||
rtc::Event event;
|
||||
int scaled_up = 0;
|
||||
int scaled_down = 0;
|
||||
};
|
||||
|
||||
// Pass a lower sampling period to speed up the tests.
|
||||
class QualityScalerUnderTest : public QualityScaler {
|
||||
public:
|
||||
explicit QualityScalerUnderTest(ScalingObserverInterface* observer,
|
||||
VideoEncoder::QpThresholds thresholds)
|
||||
: QualityScaler(observer, thresholds, 5) {}
|
||||
};
|
||||
|
||||
class QualityScalerTest : public ::testing::Test {
|
||||
protected:
|
||||
enum ScaleDirection {
|
||||
@ -41,346 +62,116 @@ class QualityScalerTest : public ::testing::Test {
|
||||
kScaleUp
|
||||
};
|
||||
|
||||
QualityScalerTest() {
|
||||
input_frame_ = I420Buffer::Create(kWidth, kHeight);
|
||||
qs_.Init(kLowQpThreshold, kHighQp, 0, kWidth, kHeight, kFramerate);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScalerTest()
|
||||
: q_(new rtc::TaskQueue("QualityScalerTestQueue")),
|
||||
observer_(new MockScaleObserver()) {
|
||||
rtc::Event event(false, false);
|
||||
q_->PostTask([this, &event] {
|
||||
qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
|
||||
observer_.get(),
|
||||
VideoEncoder::QpThresholds(kLowQpThreshold, kHighQp)));
|
||||
event.Set();
|
||||
});
|
||||
EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
|
||||
}
|
||||
|
||||
bool TriggerScale(ScaleDirection scale_direction) {
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
int initial_width = qs_.GetScaledResolution().width;
|
||||
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
|
||||
~QualityScalerTest() {
|
||||
rtc::Event event(false, false);
|
||||
q_->PostTask([this, &event] {
|
||||
qs_.reset(nullptr);
|
||||
event.Set();
|
||||
});
|
||||
EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
|
||||
}
|
||||
|
||||
void TriggerScale(ScaleDirection scale_direction) {
|
||||
for (int i = 0; i < kFramerate * 5; ++i) {
|
||||
switch (scale_direction) {
|
||||
case kScaleUp:
|
||||
qs_.ReportQP(kLowQp);
|
||||
qs_->ReportQP(kLowQp);
|
||||
break;
|
||||
case kScaleDown:
|
||||
qs_.ReportDroppedFrame();
|
||||
qs_->ReportDroppedFrame();
|
||||
break;
|
||||
case kKeepScaleAtHighQp:
|
||||
qs_.ReportQP(kHighQp);
|
||||
qs_->ReportQP(kHighQp);
|
||||
break;
|
||||
case kScaleDownAboveHighQp:
|
||||
qs_.ReportQP(kHighQp + 1);
|
||||
qs_->ReportQP(kHighQp + 1);
|
||||
break;
|
||||
}
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
if (qs_.GetScaledResolution().width != initial_width)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExpectOriginalFrame() {
|
||||
EXPECT_EQ(input_frame_, qs_.GetScaledBuffer(input_frame_))
|
||||
<< "Using scaled frame instead of original input.";
|
||||
}
|
||||
|
||||
void ExpectScaleUsingReportedResolution() {
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
rtc::scoped_refptr<VideoFrameBuffer> scaled_frame =
|
||||
qs_.GetScaledBuffer(input_frame_);
|
||||
EXPECT_EQ(res.width, scaled_frame->width());
|
||||
EXPECT_EQ(res.height, scaled_frame->height());
|
||||
}
|
||||
|
||||
void ContinuouslyDownscalesByHalfDimensionsAndBackUp();
|
||||
|
||||
void DoesNotDownscaleFrameDimensions(int width, int height);
|
||||
|
||||
void DownscaleEndsAt(int input_width,
|
||||
int input_height,
|
||||
int end_width,
|
||||
int end_height);
|
||||
|
||||
QualityScaler qs_;
|
||||
rtc::scoped_refptr<VideoFrameBuffer> input_frame_;
|
||||
std::unique_ptr<rtc::TaskQueue> q_;
|
||||
std::unique_ptr<QualityScaler> qs_;
|
||||
std::unique_ptr<MockScaleObserver> observer_;
|
||||
};
|
||||
|
||||
TEST_F(QualityScalerTest, UsesOriginalFrameInitially) {
|
||||
ExpectOriginalFrame();
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, ReportsOriginalResolutionInitially) {
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
EXPECT_EQ(input_frame_->width(), res.width);
|
||||
EXPECT_EQ(input_frame_->height(), res.height);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
|
||||
EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " << kNumSeconds
|
||||
<< " seconds.";
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
EXPECT_LT(res.width, input_frame_->width());
|
||||
EXPECT_LT(res.height, input_frame_->height());
|
||||
q_->PostTask([this] { TriggerScale(kScaleDown); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, KeepsScaleAtHighQp) {
|
||||
EXPECT_FALSE(TriggerScale(kKeepScaleAtHighQp))
|
||||
<< "Downscale at high threshold which should keep scale.";
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
EXPECT_EQ(res.width, input_frame_->width());
|
||||
EXPECT_EQ(res.height, input_frame_->height());
|
||||
q_->PostTask([this] { TriggerScale(kKeepScaleAtHighQp); });
|
||||
EXPECT_FALSE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(0, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesAboveHighQp) {
|
||||
EXPECT_TRUE(TriggerScale(kScaleDownAboveHighQp))
|
||||
<< "No downscale within " << kNumSeconds << " seconds.";
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
EXPECT_LT(res.width, input_frame_->width());
|
||||
EXPECT_LT(res.height, input_frame_->height());
|
||||
q_->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
|
||||
for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) {
|
||||
qs_.ReportQP(kNormalQp);
|
||||
qs_.ReportDroppedFrame();
|
||||
qs_.ReportDroppedFrame();
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
if (qs_.GetScaledResolution().width < input_frame_->width())
|
||||
return;
|
||||
}
|
||||
|
||||
FAIL() << "No downscale within " << kNumSeconds << " seconds.";
|
||||
q_->PostTask([this] {
|
||||
qs_->ReportDroppedFrame();
|
||||
qs_->ReportDroppedFrame();
|
||||
qs_->ReportQP(kHighQp);
|
||||
});
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) {
|
||||
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
|
||||
qs_.ReportQP(kNormalQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width)
|
||||
<< "Unexpected scale on half framedrop.";
|
||||
}
|
||||
q_->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
|
||||
for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) {
|
||||
qs_.ReportQP(kNormalQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width)
|
||||
<< "Unexpected scale on half framedrop.";
|
||||
|
||||
qs_.ReportDroppedFrame();
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width)
|
||||
<< "Unexpected scale on half framedrop.";
|
||||
}
|
||||
q_->PostTask([this] {
|
||||
qs_->ReportDroppedFrame();
|
||||
qs_->ReportQP(kHighQp);
|
||||
});
|
||||
EXPECT_FALSE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(0, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
}
|
||||
|
||||
void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() {
|
||||
const int initial_min_dimension =
|
||||
input_frame_->width() < input_frame_->height() ? input_frame_->width()
|
||||
: input_frame_->height();
|
||||
int min_dimension = initial_min_dimension;
|
||||
int current_shift = 0;
|
||||
// Drop all frames to force-trigger downscaling.
|
||||
while (min_dimension >= 2 * kMinDownscaleDimension) {
|
||||
EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within "
|
||||
<< kNumSeconds << " seconds.";
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
min_dimension = res.width < res.height ? res.width : res.height;
|
||||
++current_shift;
|
||||
ASSERT_EQ(input_frame_->width() >> current_shift, res.width);
|
||||
ASSERT_EQ(input_frame_->height() >> current_shift, res.height);
|
||||
ExpectScaleUsingReportedResolution();
|
||||
}
|
||||
|
||||
// Make sure we can scale back with good-quality frames.
|
||||
while (min_dimension < initial_min_dimension) {
|
||||
EXPECT_TRUE(TriggerScale(kScaleUp)) << "No upscale within " << kNumSeconds
|
||||
<< " seconds.";
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
min_dimension = res.width < res.height ? res.width : res.height;
|
||||
--current_shift;
|
||||
ASSERT_EQ(input_frame_->width() >> current_shift, res.width);
|
||||
ASSERT_EQ(input_frame_->height() >> current_shift, res.height);
|
||||
ExpectScaleUsingReportedResolution();
|
||||
}
|
||||
|
||||
// Verify we don't start upscaling after further low use.
|
||||
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
|
||||
qs_.ReportQP(kLowQp);
|
||||
ExpectOriginalFrame();
|
||||
}
|
||||
TEST_F(QualityScalerTest, UpscalesAfterLowQp) {
|
||||
q_->PostTask([this] { TriggerScale(kScaleUp); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(0, observer_->scaled_down);
|
||||
EXPECT_EQ(1, observer_->scaled_up);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) {
|
||||
ContinuouslyDownscalesByHalfDimensionsAndBackUp();
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest,
|
||||
ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) {
|
||||
const int kOddWidth = 517;
|
||||
const int kOddHeight = 1239;
|
||||
input_frame_ = I420Buffer::Create(kOddWidth, kOddHeight);
|
||||
ContinuouslyDownscalesByHalfDimensionsAndBackUp();
|
||||
}
|
||||
|
||||
void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) {
|
||||
input_frame_ = I420Buffer::Create(width, height);
|
||||
|
||||
for (int i = 0; i < kFramerate * kNumSeconds; ++i) {
|
||||
qs_.ReportDroppedFrame();
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width)
|
||||
<< "Unexpected scale of minimal-size frame.";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) {
|
||||
DoesNotDownscaleFrameDimensions(1, kHeight);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) {
|
||||
DoesNotDownscaleFrameDimensions(kWidth, 1);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) {
|
||||
DoesNotDownscaleFrameDimensions(1, 1);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) {
|
||||
DoesNotDownscaleFrameDimensions(
|
||||
2 * kMinDownscaleDimension - 1, 1000);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) {
|
||||
DoesNotDownscaleFrameDimensions(
|
||||
1000, 2 * kMinDownscaleDimension - 1);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) {
|
||||
static const int kWidth720p = 1280;
|
||||
static const int kHeight720p = 720;
|
||||
static const int kInitialBitrateKbps = 300;
|
||||
input_frame_ = I420Buffer::Create(kWidth720p, kHeight720p);
|
||||
qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps,
|
||||
kWidth720p, kHeight720p, kFramerate);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
int init_width = qs_.GetScaledResolution().width;
|
||||
int init_height = qs_.GetScaledResolution().height;
|
||||
EXPECT_EQ(640, init_width);
|
||||
EXPECT_EQ(360, init_height);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscaleToQvgaOnLowerInitialBitrate) {
|
||||
static const int kWidth720p = 1280;
|
||||
static const int kHeight720p = 720;
|
||||
static const int kInitialBitrateKbps = 200;
|
||||
input_frame_ = I420Buffer::Create(kWidth720p, kHeight720p);
|
||||
qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps,
|
||||
kWidth720p, kHeight720p, kFramerate);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
int init_width = qs_.GetScaledResolution().width;
|
||||
int init_height = qs_.GetScaledResolution().height;
|
||||
EXPECT_EQ(320, init_width);
|
||||
EXPECT_EQ(180, init_height);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscaleAfterMeasuredSecondsThenSlowerBackUp) {
|
||||
qs_.Init(kLowQpThreshold, kHighQp, 0, kWidth, kHeight, kFramerate);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution initial_res = qs_.GetScaledResolution();
|
||||
|
||||
// Should not downscale if less than kMeasureSecondsDownscale seconds passed.
|
||||
for (int i = 0; i < kFramerate * kMeasureSecondsDownscale - 1; ++i) {
|
||||
qs_.ReportQP(kHighQp + 1);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
}
|
||||
EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height);
|
||||
|
||||
// Should downscale if more than kMeasureSecondsDownscale seconds passed (add
|
||||
// last frame).
|
||||
qs_.ReportQP(kHighQp + 1);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height);
|
||||
|
||||
// Should not upscale if less than kMeasureSecondsUpscale seconds passed since
|
||||
// we saw issues initially (have already gone down).
|
||||
for (int i = 0; i < kFramerate * kMeasureSecondsUpscale - 1; ++i) {
|
||||
qs_.ReportQP(kLowQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
}
|
||||
EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height);
|
||||
|
||||
// Should upscale (back to initial) if kMeasureSecondsUpscale seconds passed
|
||||
// (add last frame).
|
||||
qs_.ReportQP(kLowQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, UpscaleQuicklyInitiallyAfterMeasuredSeconds) {
|
||||
qs_.Init(kLowQpThreshold, kHighQp, kLowInitialBitrateKbps, kWidth, kHeight,
|
||||
kFramerate);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
QualityScaler::Resolution initial_res = qs_.GetScaledResolution();
|
||||
|
||||
// Should not upscale if less than kMeasureSecondsFastUpscale seconds passed.
|
||||
for (int i = 0; i < kFramerate * kMeasureSecondsFastUpscale - 1; ++i) {
|
||||
qs_.ReportQP(kLowQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
}
|
||||
EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height);
|
||||
|
||||
// Should upscale if kMeasureSecondsFastUpscale seconds passed (add last
|
||||
// frame).
|
||||
qs_.ReportQP(kLowQp);
|
||||
qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height());
|
||||
EXPECT_LT(initial_res.width, qs_.GetScaledResolution().width);
|
||||
EXPECT_LT(initial_res.height, qs_.GetScaledResolution().height);
|
||||
}
|
||||
|
||||
void QualityScalerTest::DownscaleEndsAt(int input_width,
|
||||
int input_height,
|
||||
int end_width,
|
||||
int end_height) {
|
||||
// Create a frame with 2x expected end width/height to verify that we can
|
||||
// scale down to expected end width/height.
|
||||
input_frame_ = I420Buffer::Create(input_width, input_height);
|
||||
|
||||
int last_width = input_width;
|
||||
int last_height = input_height;
|
||||
// Drop all frames to force-trigger downscaling.
|
||||
while (true) {
|
||||
TriggerScale(kScaleDown);
|
||||
QualityScaler::Resolution res = qs_.GetScaledResolution();
|
||||
if (last_width == res.width) {
|
||||
EXPECT_EQ(last_height, res.height);
|
||||
EXPECT_EQ(end_width, res.width);
|
||||
EXPECT_EQ(end_height, res.height);
|
||||
break;
|
||||
}
|
||||
last_width = res.width;
|
||||
last_height = res.height;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesTo320x180) {
|
||||
DownscaleEndsAt(640, 360, 320, 180);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesTo180x320) {
|
||||
DownscaleEndsAt(360, 640, 180, 320);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DownscalesFrom1280x720To320x180) {
|
||||
DownscaleEndsAt(1280, 720, 320, 180);
|
||||
}
|
||||
|
||||
TEST_F(QualityScalerTest, DoesntDownscaleInitialQvga) {
|
||||
DownscaleEndsAt(320, 180, 320, 180);
|
||||
TEST_F(QualityScalerTest, ScalesDownAndBackUp) {
|
||||
q_->PostTask([this] { TriggerScale(kScaleDown); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
EXPECT_EQ(0, observer_->scaled_up);
|
||||
q_->PostTask([this] { TriggerScale(kScaleUp); });
|
||||
EXPECT_TRUE(observer_->event.Wait(50));
|
||||
EXPECT_EQ(1, observer_->scaled_down);
|
||||
EXPECT_EQ(1, observer_->scaled_up);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Reference in New Issue
Block a user