1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 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 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/modules/video_coding/utility/quality_scaler.h" 12 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace webrtc { 16 namespace { 17 static const int kNumSeconds = 10; 18 static const int kWidth = 1920; 19 static const int kHalfWidth = kWidth / 2; 20 static const int kHeight = 1080; 21 static const int kFramerate = 30; 22 static const int kLowQp = 15; 23 static const int kNormalQp = 30; 24 static const int kMaxQp = 56; 25 } // namespace 26 27 class QualityScalerTest : public ::testing::Test { 28 protected: 29 enum ScaleDirection { kScaleDown, kScaleUp }; 30 31 QualityScalerTest() { 32 input_frame_.CreateEmptyFrame( 33 kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth); 34 qs_.Init(kMaxQp); 35 qs_.ReportFramerate(kFramerate); 36 } 37 38 void TriggerScale(ScaleDirection scale_direction) { 39 int initial_width = qs_.GetScaledResolution(input_frame_).width; 40 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { 41 switch (scale_direction) { 42 case kScaleUp: 43 qs_.ReportEncodedFrame(kLowQp); 44 break; 45 case kScaleDown: 46 qs_.ReportDroppedFrame(); 47 break; 48 } 49 50 if (qs_.GetScaledResolution(input_frame_).width != initial_width) 51 return; 52 } 53 54 FAIL() << "No downscale within " << kNumSeconds << " seconds."; 55 } 56 57 void ExpectOriginalFrame() { 58 EXPECT_EQ(&input_frame_, &qs_.GetScaledFrame(input_frame_)) 59 << "Using scaled frame instead of original input."; 60 } 61 62 void ExpectScaleUsingReportedResolution() { 63 QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); 64 const I420VideoFrame& scaled_frame = qs_.GetScaledFrame(input_frame_); 65 EXPECT_EQ(res.width, scaled_frame.width()); 66 EXPECT_EQ(res.height, scaled_frame.height()); 67 } 68 69 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); 70 71 void DoesNotDownscaleFrameDimensions(int width, int height); 72 73 QualityScaler qs_; 74 I420VideoFrame input_frame_; 75 }; 76 77 TEST_F(QualityScalerTest, UsesOriginalFrameInitially) { 78 ExpectOriginalFrame(); 79 } 80 81 TEST_F(QualityScalerTest, ReportsOriginalResolutionInitially) { 82 QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); 83 EXPECT_EQ(input_frame_.width(), res.width); 84 EXPECT_EQ(input_frame_.height(), res.height); 85 } 86 87 TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) { 88 TriggerScale(kScaleDown); 89 QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); 90 EXPECT_LT(res.width, input_frame_.width()); 91 EXPECT_LT(res.height, input_frame_.height()); 92 } 93 94 TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { 95 for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) { 96 qs_.ReportEncodedFrame(kNormalQp); 97 qs_.ReportDroppedFrame(); 98 qs_.ReportDroppedFrame(); 99 if (qs_.GetScaledResolution(input_frame_).width < input_frame_.width()) 100 return; 101 } 102 103 FAIL() << "No downscale within " << kNumSeconds << " seconds."; 104 } 105 106 TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { 107 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { 108 qs_.ReportEncodedFrame(kNormalQp); 109 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) 110 << "Unexpected scale on half framedrop."; 111 } 112 } 113 114 TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) { 115 for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) { 116 qs_.ReportEncodedFrame(kNormalQp); 117 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) 118 << "Unexpected scale on half framedrop."; 119 120 qs_.ReportDroppedFrame(); 121 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) 122 << "Unexpected scale on half framedrop."; 123 } 124 } 125 126 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { 127 const int initial_min_dimension = input_frame_.width() < input_frame_.height() 128 ? input_frame_.width() 129 : input_frame_.height(); 130 int min_dimension = initial_min_dimension; 131 int current_shift = 0; 132 // Drop all frames to force-trigger downscaling. 133 while (min_dimension > 16) { 134 TriggerScale(kScaleDown); 135 QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); 136 min_dimension = res.width < res.height ? res.width : res.height; 137 ++current_shift; 138 ASSERT_EQ(input_frame_.width() >> current_shift, res.width); 139 ASSERT_EQ(input_frame_.height() >> current_shift, res.height); 140 ExpectScaleUsingReportedResolution(); 141 } 142 143 // Make sure we can scale back with good-quality frames. 144 while (min_dimension < initial_min_dimension) { 145 TriggerScale(kScaleUp); 146 QualityScaler::Resolution res = qs_.GetScaledResolution(input_frame_); 147 min_dimension = res.width < res.height ? res.width : res.height; 148 --current_shift; 149 ASSERT_EQ(input_frame_.width() >> current_shift, res.width); 150 ASSERT_EQ(input_frame_.height() >> current_shift, res.height); 151 ExpectScaleUsingReportedResolution(); 152 } 153 154 // Verify we don't start upscaling after further low use. 155 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { 156 qs_.ReportEncodedFrame(kLowQp); 157 ExpectOriginalFrame(); 158 } 159 } 160 161 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { 162 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); 163 } 164 165 TEST_F(QualityScalerTest, 166 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { 167 const int kOddWidth = 517; 168 const int kHalfOddWidth = (kOddWidth + 1) / 2; 169 const int kOddHeight = 1239; 170 input_frame_.CreateEmptyFrame( 171 kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, kHalfOddWidth); 172 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); 173 } 174 175 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { 176 input_frame_.CreateEmptyFrame( 177 width, height, width, (width + 1) / 2, (width + 1) / 2); 178 179 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { 180 qs_.ReportDroppedFrame(); 181 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) 182 << "Unexpected scale of minimal-size frame."; 183 } 184 } 185 186 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { 187 DoesNotDownscaleFrameDimensions(1, kHeight); 188 } 189 190 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { 191 DoesNotDownscaleFrameDimensions(kWidth, 1); 192 } 193 194 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { 195 DoesNotDownscaleFrameDimensions(1, 1); 196 } 197 198 } // namespace webrtc 199