Index: webrtc/modules/video_coding/utility/quality_scaler_unittest.cc |
diff --git a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc |
index 24d5c48d38a3fe2b6424832a99daee1c0c041f65..5ac7dc84ae3fcaef3d5e9fd03d31bbd43f94fbcb 100644 |
--- a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc |
+++ b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc |
@@ -10,28 +10,38 @@ |
#include "webrtc/modules/video_coding/utility/quality_scaler.h" |
+#include <memory> |
+ |
+#include "webrtc/base/event.h" |
+#include "webrtc/base/task_queue.h" |
+#include "webrtc/common_video/rotation.h" |
+#include "webrtc/video_frame.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; |
} // namespace |
+class MockScaleObserver : public ScalingInterface { |
+ public: |
+ MockScaleObserver() {} |
+ virtual ~MockScaleObserver() {} |
+ |
+ MOCK_METHOD0(ScaleUp, void()); |
+ MOCK_METHOD0(ScaleDown, void()); |
+}; |
+ |
+// Override GetTimeoutMs to speed up the tests. |
+class QualityScalerUnderTest : public QualityScaler { |
+ public: |
+ int64_t GetTimeoutMs() { return 5; } |
+}; |
+ |
class QualityScalerTest : public ::testing::Test { |
protected: |
enum ScaleDirection { |
@@ -41,346 +51,119 @@ 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")), |
+ qs_(new QualityScalerUnderTest), |
+ observer_(new MockScaleObserver()) { |
+ rtc::Event event(false, false); |
+ q->PostTask([this, &event] { |
+ qs_->Init(observer_.get(), kLowQpThreshold, kHighQp, kFramerate); |
+ event.Set(); |
+ }); |
+ EXPECT_TRUE(event.Wait(1000)); |
+ } |
+ |
+ ~QualityScalerTest() { |
+ rtc::Event event(false, false); |
+ q->PostTask([this, &event] { |
+ qs_->Stop(); |
+ event.Set(); |
+ }); |
+ EXPECT_TRUE(event.Wait(1000)); |
} |
- 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) { |
+ 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(); |
perkj_webrtc
2016/10/07 07:41:16
Didn't you remove use of ReportDroppedFrame?
kthelgason
2016/10/07 09:34:48
No, at least that was not the intention.
|
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); |
-} |
+#define WAIT_FOR_MS(x) \ |
+ do { \ |
+ rtc::Event ev(false, false); \ |
+ ev.Wait(x); \ |
+ } while (0) |
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()); |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(1); |
+ q->PostTask([this] { TriggerScale(kScaleDown); }); |
+ WAIT_FOR_MS(50); |
} |
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()); |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(0); |
+ EXPECT_CALL(*observer_, ScaleUp()).Times(0); |
+ q->PostTask([this] { TriggerScale(kKeepScaleAtHighQp); }); |
+ WAIT_FOR_MS(50); |
} |
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()); |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(1); |
+ q->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); }); |
+ WAIT_FOR_MS(50); |
} |
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."; |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(1); |
+ q->PostTask([this] { |
+ qs_->ReportDroppedFrame(); |
+ qs_->ReportDroppedFrame(); |
+ qs_->ReportQP(kHighQp); |
+ }); |
+ WAIT_FOR_MS(50); |
} |
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."; |
- } |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(1); |
+ q->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); }); |
+ WAIT_FOR_MS(50); |
} |
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."; |
- } |
-} |
- |
-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, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { |
- ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(0); |
+ q->PostTask([this] { |
+ qs_->ReportDroppedFrame(); |
+ qs_->ReportQP(kHighQp); |
+ }); |
+ WAIT_FOR_MS(50); |
} |
-TEST_F(QualityScalerTest, |
- ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { |
- const int kOddWidth = 517; |
- const int kOddHeight = 1239; |
- input_frame_ = I420Buffer::Create(kOddWidth, kOddHeight); |
- ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
+TEST_F(QualityScalerTest, UpscalesAfterLowQp) { |
+ EXPECT_CALL(*observer_, ScaleUp()).Times(1); |
+ q->PostTask([this] { TriggerScale(kScaleUp); }); |
+ WAIT_FOR_MS(50); |
} |
-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, ScalesDownAndBackUp) { |
+ { |
+ testing::InSequence _; |
+ EXPECT_CALL(*observer_, ScaleDown()).Times(1); |
+ EXPECT_CALL(*observer_, ScaleUp()).Times(1); |
} |
+ q->PostTask([this] { TriggerScale(kScaleDown); }); |
+ WAIT_FOR_MS(50); |
+ q->PostTask([this] { TriggerScale(kScaleUp); }); |
+ WAIT_FOR_MS(50); |
} |
-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); |
-} |
+#undef WAIT_FOR_MS |
} // namespace webrtc |