| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 } // namespace | 26 } // namespace |
| 27 | 27 |
| 28 class QualityScalerTest : public ::testing::Test { | 28 class QualityScalerTest : public ::testing::Test { |
| 29 public: | 29 public: |
| 30 // Temporal and spatial resolution. | 30 // Temporal and spatial resolution. |
| 31 struct Resolution { | 31 struct Resolution { |
| 32 int framerate; | 32 int framerate; |
| 33 int width; | 33 int width; |
| 34 int height; | 34 int height; |
| 35 }; | 35 }; |
| 36 |
| 36 protected: | 37 protected: |
| 37 enum ScaleDirection { | 38 enum ScaleDirection { |
| 38 kKeepScaleAtHighQp, | 39 kKeepScaleAtHighQp, |
| 39 kScaleDown, | 40 kScaleDown, |
| 40 kScaleDownAboveHighQp, | 41 kScaleDownAboveHighQp, |
| 41 kScaleUp | 42 kScaleUp |
| 42 }; | 43 }; |
| 43 enum BadQualityMetric { kDropFrame, kReportLowQP }; | 44 enum BadQualityMetric { kDropFrame, kReportLowQP }; |
| 44 | 45 |
| 45 QualityScalerTest() { | 46 QualityScalerTest() { |
| 46 input_frame_.CreateEmptyFrame( | 47 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, |
| 47 kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth); | 48 kHalfWidth); |
| 48 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false); | 49 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false); |
| 49 qs_.ReportFramerate(kFramerate); | 50 qs_.ReportFramerate(kFramerate); |
| 50 qs_.OnEncodeFrame(input_frame_); | 51 qs_.OnEncodeFrame(input_frame_); |
| 51 } | 52 } |
| 52 | 53 |
| 53 bool TriggerScale(ScaleDirection scale_direction) { | 54 bool TriggerScale(ScaleDirection scale_direction) { |
| 54 qs_.OnEncodeFrame(input_frame_); | 55 qs_.OnEncodeFrame(input_frame_); |
| 55 int initial_width = qs_.GetScaledResolution().width; | 56 int initial_width = qs_.GetScaledResolution().width; |
| 56 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 57 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
| 57 switch (scale_direction) { | 58 switch (scale_direction) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 } | 91 } |
| 91 | 92 |
| 92 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 93 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
| 93 | 94 |
| 94 void DoesNotDownscaleFrameDimensions(int width, int height); | 95 void DoesNotDownscaleFrameDimensions(int width, int height); |
| 95 | 96 |
| 96 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, | 97 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, |
| 97 int num_second, | 98 int num_second, |
| 98 int initial_framerate); | 99 int initial_framerate); |
| 99 | 100 |
| 100 void VerifyQualityAdaptation(int initial_framerate, int seconds, | 101 void VerifyQualityAdaptation(int initial_framerate, |
| 102 int seconds, |
| 101 bool expect_spatial_resize, | 103 bool expect_spatial_resize, |
| 102 bool expect_framerate_reduction); | 104 bool expect_framerate_reduction); |
| 103 | 105 |
| 104 void DownscaleEndsAt(int input_width, | 106 void DownscaleEndsAt(int input_width, |
| 105 int input_height, | 107 int input_height, |
| 106 int end_width, | 108 int end_width, |
| 107 int end_height); | 109 int end_height); |
| 108 | 110 |
| 109 QualityScaler qs_; | 111 QualityScaler qs_; |
| 110 VideoFrame input_frame_; | 112 VideoFrame input_frame_; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 | 178 |
| 177 qs_.ReportDroppedFrame(); | 179 qs_.ReportDroppedFrame(); |
| 178 qs_.OnEncodeFrame(input_frame_); | 180 qs_.OnEncodeFrame(input_frame_); |
| 179 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) | 181 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) |
| 180 << "Unexpected scale on half framedrop."; | 182 << "Unexpected scale on half framedrop."; |
| 181 } | 183 } |
| 182 } | 184 } |
| 183 | 185 |
| 184 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { | 186 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { |
| 185 const int initial_min_dimension = input_frame_.width() < input_frame_.height() | 187 const int initial_min_dimension = input_frame_.width() < input_frame_.height() |
| 186 ? input_frame_.width() | 188 ? input_frame_.width() |
| 187 : input_frame_.height(); | 189 : input_frame_.height(); |
| 188 int min_dimension = initial_min_dimension; | 190 int min_dimension = initial_min_dimension; |
| 189 int current_shift = 0; | 191 int current_shift = 0; |
| 190 // Drop all frames to force-trigger downscaling. | 192 // Drop all frames to force-trigger downscaling. |
| 191 while (min_dimension >= 2 * QualityScaler::kDefaultMinDownscaleDimension) { | 193 while (min_dimension >= 2 * QualityScaler::kDefaultMinDownscaleDimension) { |
| 192 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " | 194 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " |
| 193 << kNumSeconds << " seconds."; | 195 << kNumSeconds << " seconds."; |
| 194 qs_.OnEncodeFrame(input_frame_); | 196 qs_.OnEncodeFrame(input_frame_); |
| 195 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 197 QualityScaler::Resolution res = qs_.GetScaledResolution(); |
| 196 min_dimension = res.width < res.height ? res.width : res.height; | 198 min_dimension = res.width < res.height ? res.width : res.height; |
| 197 ++current_shift; | 199 ++current_shift; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 222 | 224 |
| 223 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { | 225 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { |
| 224 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 226 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
| 225 } | 227 } |
| 226 | 228 |
| 227 TEST_F(QualityScalerTest, | 229 TEST_F(QualityScalerTest, |
| 228 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { | 230 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { |
| 229 const int kOddWidth = 517; | 231 const int kOddWidth = 517; |
| 230 const int kHalfOddWidth = (kOddWidth + 1) / 2; | 232 const int kHalfOddWidth = (kOddWidth + 1) / 2; |
| 231 const int kOddHeight = 1239; | 233 const int kOddHeight = 1239; |
| 232 input_frame_.CreateEmptyFrame( | 234 input_frame_.CreateEmptyFrame(kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, |
| 233 kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, kHalfOddWidth); | 235 kHalfOddWidth); |
| 234 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 236 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
| 235 } | 237 } |
| 236 | 238 |
| 237 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { | 239 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { |
| 238 input_frame_.CreateEmptyFrame( | 240 input_frame_.CreateEmptyFrame(width, height, width, (width + 1) / 2, |
| 239 width, height, width, (width + 1) / 2, (width + 1) / 2); | 241 (width + 1) / 2); |
| 240 | 242 |
| 241 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 243 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
| 242 qs_.ReportDroppedFrame(); | 244 qs_.ReportDroppedFrame(); |
| 243 qs_.OnEncodeFrame(input_frame_); | 245 qs_.OnEncodeFrame(input_frame_); |
| 244 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) | 246 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) |
| 245 << "Unexpected scale of minimal-size frame."; | 247 << "Unexpected scale of minimal-size frame."; |
| 246 } | 248 } |
| 247 } | 249 } |
| 248 | 250 |
| 249 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { | 251 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { |
| 250 DoesNotDownscaleFrameDimensions(1, kHeight); | 252 DoesNotDownscaleFrameDimensions(1, kHeight); |
| 251 } | 253 } |
| 252 | 254 |
| 253 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { | 255 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { |
| 254 DoesNotDownscaleFrameDimensions(kWidth, 1); | 256 DoesNotDownscaleFrameDimensions(kWidth, 1); |
| 255 } | 257 } |
| 256 | 258 |
| 257 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { | 259 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { |
| 258 DoesNotDownscaleFrameDimensions(1, 1); | 260 DoesNotDownscaleFrameDimensions(1, 1); |
| 259 } | 261 } |
| 260 | 262 |
| 261 QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange( | 263 QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange( |
| 262 BadQualityMetric dropframe_lowqp, int num_second, int initial_framerate) { | 264 BadQualityMetric dropframe_lowqp, |
| 265 int num_second, |
| 266 int initial_framerate) { |
| 263 QualityScalerTest::Resolution res; | 267 QualityScalerTest::Resolution res; |
| 264 res.framerate = initial_framerate; | 268 res.framerate = initial_framerate; |
| 265 qs_.OnEncodeFrame(input_frame_); | 269 qs_.OnEncodeFrame(input_frame_); |
| 266 res.width = qs_.GetScaledResolution().width; | 270 res.width = qs_.GetScaledResolution().width; |
| 267 res.height = qs_.GetScaledResolution().height; | 271 res.height = qs_.GetScaledResolution().height; |
| 268 for (int i = 0; i < kFramerate * num_second; ++i) { | 272 for (int i = 0; i < kFramerate * num_second; ++i) { |
| 269 switch (dropframe_lowqp) { | 273 switch (dropframe_lowqp) { |
| 270 case kReportLowQP: | 274 case kReportLowQP: |
| 271 qs_.ReportQP(kLowQp); | 275 qs_.ReportQP(kLowQp); |
| 272 break; | 276 break; |
| 273 case kDropFrame: | 277 case kDropFrame: |
| 274 qs_.ReportDroppedFrame(); | 278 qs_.ReportDroppedFrame(); |
| 275 break; | 279 break; |
| 276 } | 280 } |
| 277 qs_.OnEncodeFrame(input_frame_); | 281 qs_.OnEncodeFrame(input_frame_); |
| 278 // Simulate the case when SetRates is called right after reducing | 282 // Simulate the case when SetRates is called right after reducing |
| 279 // framerate. | 283 // framerate. |
| 280 qs_.ReportFramerate(initial_framerate); | 284 qs_.ReportFramerate(initial_framerate); |
| 281 res.framerate = qs_.GetTargetFramerate(); | 285 res.framerate = qs_.GetTargetFramerate(); |
| 282 if (res.framerate != -1) | 286 if (res.framerate != -1) |
| 283 qs_.ReportFramerate(res.framerate); | 287 qs_.ReportFramerate(res.framerate); |
| 284 res.width = qs_.GetScaledResolution().width; | 288 res.width = qs_.GetScaledResolution().width; |
| 285 res.height = qs_.GetScaledResolution().height; | 289 res.height = qs_.GetScaledResolution().height; |
| 286 } | 290 } |
| 287 return res; | 291 return res; |
| 288 } | 292 } |
| 289 | 293 |
| 290 void QualityScalerTest::VerifyQualityAdaptation( | 294 void QualityScalerTest::VerifyQualityAdaptation( |
| 291 int initial_framerate, int seconds, bool expect_spatial_resize, | 295 int initial_framerate, |
| 296 int seconds, |
| 297 bool expect_spatial_resize, |
| 292 bool expect_framerate_reduction) { | 298 bool expect_framerate_reduction) { |
| 293 const int kDisabledBadQpThreshold = kMaxQp + 1; | 299 const int kDisabledBadQpThreshold = kMaxQp + 1; |
| 294 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 300 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
| 295 kDisabledBadQpThreshold, true); | 301 kDisabledBadQpThreshold, true); |
| 296 qs_.OnEncodeFrame(input_frame_); | 302 qs_.OnEncodeFrame(input_frame_); |
| 297 int init_width = qs_.GetScaledResolution().width; | 303 int init_width = qs_.GetScaledResolution().width; |
| 298 int init_height = qs_.GetScaledResolution().height; | 304 int init_height = qs_.GetScaledResolution().height; |
| 299 | 305 |
| 300 // Test reducing framerate by dropping frame continuously. | 306 // Test reducing framerate by dropping frame continuously. |
| 301 QualityScalerTest::Resolution res = TriggerResolutionChange( | 307 QualityScalerTest::Resolution res = |
| 302 kDropFrame, seconds, initial_framerate); | 308 TriggerResolutionChange(kDropFrame, seconds, initial_framerate); |
| 303 | 309 |
| 304 if (expect_framerate_reduction) { | 310 if (expect_framerate_reduction) { |
| 305 EXPECT_LT(res.framerate, initial_framerate); | 311 EXPECT_LT(res.framerate, initial_framerate); |
| 306 } else { | 312 } else { |
| 307 // No framerate reduction, video decimator should be disabled. | 313 // No framerate reduction, video decimator should be disabled. |
| 308 EXPECT_EQ(-1, res.framerate); | 314 EXPECT_EQ(-1, res.framerate); |
| 309 } | 315 } |
| 310 | 316 |
| 311 if (expect_spatial_resize) { | 317 if (expect_spatial_resize) { |
| 312 EXPECT_LT(res.width, init_width); | 318 EXPECT_LT(res.width, init_width); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 410 DownscaleEndsAt(1600, 800, 200, 100); | 416 DownscaleEndsAt(1600, 800, 200, 100); |
| 411 } | 417 } |
| 412 | 418 |
| 413 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { | 419 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { |
| 414 // Should end at 100x200, as height can't go lower. | 420 // Should end at 100x200, as height can't go lower. |
| 415 qs_.SetMinResolution(10, 200); | 421 qs_.SetMinResolution(10, 200); |
| 416 DownscaleEndsAt(800, 1600, 100, 200); | 422 DownscaleEndsAt(800, 1600, 100, 200); |
| 417 } | 423 } |
| 418 | 424 |
| 419 } // namespace webrtc | 425 } // namespace webrtc |
| OLD | NEW |