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 |
11 #include "webrtc/modules/video_coding/utility/quality_scaler.h" | 11 #include "webrtc/modules/video_coding/utility/quality_scaler.h" |
12 | 12 |
13 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
14 | 14 |
15 namespace webrtc { | 15 namespace webrtc { |
16 namespace { | 16 namespace { |
17 static const int kNumSeconds = 10; | 17 static const int kNumSeconds = 10; |
18 static const int kWidth = 1920; | 18 static const int kWidth = 1920; |
19 static const int kWidthVga = 640; | 19 static const int kWidthVga = 640; |
20 static const int kHalfWidth = kWidth / 2; | 20 static const int kHalfWidth = kWidth / 2; |
21 static const int kHeight = 1080; | 21 static const int kHeight = 1080; |
22 static const int kHeightVga = 480; | 22 static const int kHeightVga = 480; |
23 static const int kFramerate = 30; | 23 static const int kFramerate = 30; |
24 static const int kLowQp = 15; | 24 static const int kLowQp = 15; |
25 static const int kNormalQp = 30; | 25 static const int kNormalQp = 30; |
26 static const int kHighQp = 40; | 26 static const int kHighQp = 40; |
27 static const int kMaxQp = 56; | 27 static const int kMaxQp = 56; |
28 static const int kDisabledBadQpThreshold = kMaxQp + 1; | 28 static const int kDisabledBadQpThreshold = kMaxQp + 1; |
29 static const int kLowInitialBitrateKbps = 300; | 29 static const int kLowInitialBitrateKbps = 300; |
| 30 // These values need to be in sync with corresponding constants |
| 31 // in quality_scaler.cc |
| 32 static const int kMeasureSecondsDownscale = 3; |
| 33 static const int kMeasureSecondsFastUpscale = 2; |
| 34 static const int kMeasureSecondsUpscale = 5; |
30 } // namespace | 35 } // namespace |
31 | 36 |
32 class QualityScalerTest : public ::testing::Test { | 37 class QualityScalerTest : public ::testing::Test { |
33 public: | 38 public: |
34 // Temporal and spatial resolution. | 39 // Temporal and spatial resolution. |
35 struct Resolution { | 40 struct Resolution { |
36 int framerate; | 41 int framerate; |
37 int width; | 42 int width; |
38 int height; | 43 int height; |
39 }; | 44 }; |
40 | 45 |
41 protected: | 46 protected: |
42 enum ScaleDirection { | 47 enum ScaleDirection { |
43 kKeepScaleAtHighQp, | 48 kKeepScaleAtHighQp, |
44 kScaleDown, | 49 kScaleDown, |
45 kScaleDownAboveHighQp, | 50 kScaleDownAboveHighQp, |
46 kScaleUp | 51 kScaleUp |
47 }; | 52 }; |
48 enum BadQualityMetric { kDropFrame, kReportLowQP }; | 53 enum BadQualityMetric { kDropFrame, kReportLowQP }; |
49 | 54 |
50 QualityScalerTest() { | 55 QualityScalerTest() { |
51 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, | 56 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, |
52 kHalfWidth); | 57 kHalfWidth); |
53 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, | 58 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, |
54 0, 0, 0); | 59 0, 0, 0, kFramerate); |
55 qs_.ReportFramerate(kFramerate); | |
56 qs_.OnEncodeFrame(input_frame_); | 60 qs_.OnEncodeFrame(input_frame_); |
57 } | 61 } |
58 | 62 |
59 bool TriggerScale(ScaleDirection scale_direction) { | 63 bool TriggerScale(ScaleDirection scale_direction) { |
60 qs_.OnEncodeFrame(input_frame_); | 64 qs_.OnEncodeFrame(input_frame_); |
61 int initial_width = qs_.GetScaledResolution().width; | 65 int initial_width = qs_.GetScaledResolution().width; |
62 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 66 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
63 switch (scale_direction) { | 67 switch (scale_direction) { |
64 case kScaleUp: | 68 case kScaleUp: |
65 qs_.ReportQP(kLowQp); | 69 qs_.ReportQP(kLowQp); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 | 101 |
98 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 102 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
99 | 103 |
100 void DoesNotDownscaleFrameDimensions(int width, int height); | 104 void DoesNotDownscaleFrameDimensions(int width, int height); |
101 | 105 |
102 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, | 106 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, |
103 int num_second, | 107 int num_second, |
104 int initial_framerate); | 108 int initial_framerate); |
105 | 109 |
106 void VerifyQualityAdaptation(int initial_framerate, | 110 void VerifyQualityAdaptation(int initial_framerate, |
107 int seconds, | 111 int seconds_downscale, |
| 112 int seconds_upscale, |
108 bool expect_spatial_resize, | 113 bool expect_spatial_resize, |
109 bool expect_framerate_reduction); | 114 bool expect_framerate_reduction); |
110 | 115 |
111 void DownscaleEndsAt(int input_width, | 116 void DownscaleEndsAt(int input_width, |
112 int input_height, | 117 int input_height, |
113 int end_width, | 118 int end_width, |
114 int end_height); | 119 int end_height); |
115 | 120 |
116 QualityScaler qs_; | 121 QualityScaler qs_; |
117 VideoFrame input_frame_; | 122 VideoFrame input_frame_; |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 if (res.framerate != -1) | 296 if (res.framerate != -1) |
292 qs_.ReportFramerate(res.framerate); | 297 qs_.ReportFramerate(res.framerate); |
293 res.width = qs_.GetScaledResolution().width; | 298 res.width = qs_.GetScaledResolution().width; |
294 res.height = qs_.GetScaledResolution().height; | 299 res.height = qs_.GetScaledResolution().height; |
295 } | 300 } |
296 return res; | 301 return res; |
297 } | 302 } |
298 | 303 |
299 void QualityScalerTest::VerifyQualityAdaptation( | 304 void QualityScalerTest::VerifyQualityAdaptation( |
300 int initial_framerate, | 305 int initial_framerate, |
301 int seconds, | 306 int seconds_downscale, |
| 307 int seconds_upscale, |
302 bool expect_spatial_resize, | 308 bool expect_spatial_resize, |
303 bool expect_framerate_reduction) { | 309 bool expect_framerate_reduction) { |
304 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 310 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
305 kDisabledBadQpThreshold, true, 0, 0, 0); | 311 kDisabledBadQpThreshold, true, 0, 0, 0, initial_framerate); |
306 qs_.OnEncodeFrame(input_frame_); | 312 qs_.OnEncodeFrame(input_frame_); |
307 int init_width = qs_.GetScaledResolution().width; | 313 int init_width = qs_.GetScaledResolution().width; |
308 int init_height = qs_.GetScaledResolution().height; | 314 int init_height = qs_.GetScaledResolution().height; |
309 | 315 |
310 // Test reducing framerate by dropping frame continuously. | 316 // Test reducing framerate by dropping frame continuously. |
311 QualityScalerTest::Resolution res = | 317 QualityScalerTest::Resolution res = |
312 TriggerResolutionChange(kDropFrame, seconds, initial_framerate); | 318 TriggerResolutionChange(kDropFrame, seconds_downscale, initial_framerate); |
313 | 319 |
314 if (expect_framerate_reduction) { | 320 if (expect_framerate_reduction) { |
315 EXPECT_LT(res.framerate, initial_framerate); | 321 EXPECT_LT(res.framerate, initial_framerate); |
316 } else { | 322 } else { |
317 // No framerate reduction, video decimator should be disabled. | 323 // No framerate reduction, video decimator should be disabled. |
318 EXPECT_EQ(-1, res.framerate); | 324 EXPECT_EQ(-1, res.framerate); |
319 } | 325 } |
320 | 326 |
321 if (expect_spatial_resize) { | 327 if (expect_spatial_resize) { |
322 EXPECT_LT(res.width, init_width); | 328 EXPECT_LT(res.width, init_width); |
323 EXPECT_LT(res.height, init_height); | 329 EXPECT_LT(res.height, init_height); |
324 } else { | 330 } else { |
325 EXPECT_EQ(init_width, res.width); | 331 EXPECT_EQ(init_width, res.width); |
326 EXPECT_EQ(init_height, res.height); | 332 EXPECT_EQ(init_height, res.height); |
327 } | 333 } |
328 | 334 |
329 // The "seconds * 1.5" is to ensure spatial resolution to recover. | 335 // The "seconds * 1.5" is to ensure spatial resolution to recover. |
330 // For example, in 10 seconds test, framerate reduction happens in the first | 336 // For example, in 6 seconds test, framerate reduction happens in the first |
331 // 5 seconds from 30fps to 15fps and causes the buffer size to be half of the | 337 // 3 seconds from 30fps to 15fps and causes the buffer size to be half of the |
332 // original one. Then it will take only 75 samples to downscale (twice in 150 | 338 // original one. Then it will take only 45 samples to downscale (twice in 90 |
333 // samples). So to recover the resolution changes, we need more than 10 | 339 // samples). So to recover the resolution changes, we need more than 10 |
334 // seconds (i.e, seconds * 1.5). This is because the framerate increases | 340 // seconds (i.e, seconds_upscale * 1.5). This is because the framerate |
335 // before spatial size recovers, so it will take 150 samples to recover | 341 // increases before spatial size recovers, so it will take 150 samples to |
336 // spatial size (300 for twice). | 342 // recover spatial size (300 for twice). |
337 res = TriggerResolutionChange(kReportLowQP, seconds * 1.5, initial_framerate); | 343 res = TriggerResolutionChange(kReportLowQP, seconds_upscale * 1.5, |
| 344 initial_framerate); |
338 EXPECT_EQ(-1, res.framerate); | 345 EXPECT_EQ(-1, res.framerate); |
339 EXPECT_EQ(init_width, res.width); | 346 EXPECT_EQ(init_width, res.width); |
340 EXPECT_EQ(init_height, res.height); | 347 EXPECT_EQ(init_height, res.height); |
341 } | 348 } |
342 | 349 |
343 // In 5 seconds test, only framerate adjusting should happen. | 350 // In 3 seconds test, only framerate adjusting should happen and 5 second |
| 351 // upscaling duration, only a framerate adjusting should happen. |
344 TEST_F(QualityScalerTest, ChangeFramerateOnly) { | 352 TEST_F(QualityScalerTest, ChangeFramerateOnly) { |
345 VerifyQualityAdaptation(kFramerate, 5, false, true); | 353 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale, |
| 354 kMeasureSecondsUpscale, false, true); |
346 } | 355 } |
347 | 356 |
348 // In 10 seconds test, framerate adjusting and scaling are both | 357 // In 6 seconds test, framerate adjusting and scaling are both |
349 // triggered, it shows that scaling would happen after framerate | 358 // triggered, it shows that scaling would happen after framerate |
350 // adjusting. | 359 // adjusting. |
351 TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) { | 360 TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) { |
352 VerifyQualityAdaptation(kFramerate, 10, true, true); | 361 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale * 2, |
| 362 kMeasureSecondsUpscale * 2, true, true); |
353 } | 363 } |
354 | 364 |
355 // When starting from a low framerate, only spatial size will be changed. | 365 // When starting from a low framerate, only spatial size will be changed. |
356 TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) { | 366 TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) { |
357 qs_.ReportFramerate(kFramerate >> 1); | 367 qs_.ReportFramerate(kFramerate >> 1); |
358 VerifyQualityAdaptation(kFramerate >> 1, 10, true, false); | 368 VerifyQualityAdaptation(kFramerate >> 1, kMeasureSecondsDownscale * 2, |
| 369 kMeasureSecondsUpscale * 2, true, false); |
359 } | 370 } |
360 | 371 |
361 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { | 372 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { |
362 DoesNotDownscaleFrameDimensions( | 373 DoesNotDownscaleFrameDimensions( |
363 2 * QualityScaler::kDefaultMinDownscaleDimension - 1, 1000); | 374 2 * QualityScaler::kDefaultMinDownscaleDimension - 1, 1000); |
364 } | 375 } |
365 | 376 |
366 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { | 377 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { |
367 DoesNotDownscaleFrameDimensions( | 378 DoesNotDownscaleFrameDimensions( |
368 1000, 2 * QualityScaler::kDefaultMinDownscaleDimension - 1); | 379 1000, 2 * QualityScaler::kDefaultMinDownscaleDimension - 1); |
369 } | 380 } |
370 | 381 |
371 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { | 382 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { |
372 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 383 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
373 kDisabledBadQpThreshold, true, | 384 kDisabledBadQpThreshold, true, |
374 kLowInitialBitrateKbps, kWidth, kHeight); | 385 kLowInitialBitrateKbps, kWidth, kHeight, kFramerate); |
375 qs_.OnEncodeFrame(input_frame_); | 386 qs_.OnEncodeFrame(input_frame_); |
376 int init_width = qs_.GetScaledResolution().width; | 387 int init_width = qs_.GetScaledResolution().width; |
377 int init_height = qs_.GetScaledResolution().height; | 388 int init_height = qs_.GetScaledResolution().height; |
378 EXPECT_LE(init_width, kWidthVga); | 389 EXPECT_LE(init_width, kWidthVga); |
379 EXPECT_LE(init_height, kHeightVga); | 390 EXPECT_LE(init_height, kHeightVga); |
380 } | 391 } |
381 | 392 |
| 393 TEST_F(QualityScalerTest, DownscaleAfterMeasuredSecondsThenSlowerBackUp) { |
| 394 QualityScalerTest::Resolution initial_res; |
| 395 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, 0, |
| 396 kWidth, kHeight, kFramerate); |
| 397 qs_.OnEncodeFrame(input_frame_); |
| 398 initial_res.width = qs_.GetScaledResolution().width; |
| 399 initial_res.height = qs_.GetScaledResolution().height; |
| 400 |
| 401 // Should not downscale if less than kMeasureSecondsDownscale seconds passed. |
| 402 for (int i = 0; i < kFramerate * kMeasureSecondsDownscale - 1; ++i) { |
| 403 qs_.ReportQP(kHighQp + 1); |
| 404 qs_.OnEncodeFrame(input_frame_); |
| 405 } |
| 406 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
| 407 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
| 408 |
| 409 // Should downscale if more than kMeasureSecondsDownscale seconds passed (add |
| 410 // last frame). |
| 411 qs_.ReportQP(kHighQp + 1); |
| 412 qs_.OnEncodeFrame(input_frame_); |
| 413 EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width); |
| 414 EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height); |
| 415 |
| 416 // Should not upscale if less than kMeasureSecondsUpscale seconds passed since |
| 417 // we saw issues initially (have already gone down). |
| 418 for (int i = 0; i < kFramerate * kMeasureSecondsUpscale - 1; ++i) { |
| 419 qs_.ReportQP(kLowQp); |
| 420 qs_.OnEncodeFrame(input_frame_); |
| 421 } |
| 422 EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width); |
| 423 EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height); |
| 424 |
| 425 // Should upscale (back to initial) if kMeasureSecondsUpscale seconds passed |
| 426 // (add last frame). |
| 427 qs_.ReportQP(kLowQp); |
| 428 qs_.OnEncodeFrame(input_frame_); |
| 429 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
| 430 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
| 431 } |
| 432 |
| 433 TEST_F(QualityScalerTest, UpscaleQuicklyInitiallyAfterMeasuredSeconds) { |
| 434 QualityScalerTest::Resolution initial_res; |
| 435 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, |
| 436 kLowInitialBitrateKbps, kWidth, kHeight, kFramerate); |
| 437 qs_.OnEncodeFrame(input_frame_); |
| 438 initial_res.width = qs_.GetScaledResolution().width; |
| 439 initial_res.height = qs_.GetScaledResolution().height; |
| 440 |
| 441 // Should not upscale if less than kMeasureSecondsFastUpscale seconds passed. |
| 442 for (int i = 0; i < kFramerate * kMeasureSecondsFastUpscale - 1; ++i) { |
| 443 qs_.ReportQP(kLowQp); |
| 444 qs_.OnEncodeFrame(input_frame_); |
| 445 } |
| 446 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
| 447 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
| 448 |
| 449 // Should upscale if kMeasureSecondsFastUpscale seconds passed (add last |
| 450 // frame). |
| 451 qs_.ReportQP(kLowQp); |
| 452 qs_.OnEncodeFrame(input_frame_); |
| 453 EXPECT_LT(initial_res.width, qs_.GetScaledResolution().width); |
| 454 EXPECT_LT(initial_res.height, qs_.GetScaledResolution().height); |
| 455 } |
| 456 |
382 void QualityScalerTest::DownscaleEndsAt(int input_width, | 457 void QualityScalerTest::DownscaleEndsAt(int input_width, |
383 int input_height, | 458 int input_height, |
384 int end_width, | 459 int end_width, |
385 int end_height) { | 460 int end_height) { |
386 // Create a frame with 2x expected end width/height to verify that we can | 461 // Create a frame with 2x expected end width/height to verify that we can |
387 // scale down to expected end width/height. | 462 // scale down to expected end width/height. |
388 input_frame_.CreateEmptyFrame(input_width, input_height, input_width, | 463 input_frame_.CreateEmptyFrame(input_width, input_height, input_width, |
389 (input_width + 1) / 2, (input_width + 1) / 2); | 464 (input_width + 1) / 2, (input_width + 1) / 2); |
390 | 465 |
391 int last_width = input_width; | 466 int last_width = input_width; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 DownscaleEndsAt(1600, 800, 200, 100); | 506 DownscaleEndsAt(1600, 800, 200, 100); |
432 } | 507 } |
433 | 508 |
434 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { | 509 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { |
435 // Should end at 100x200, as height can't go lower. | 510 // Should end at 100x200, as height can't go lower. |
436 qs_.SetMinResolution(10, 200); | 511 qs_.SetMinResolution(10, 200); |
437 DownscaleEndsAt(800, 1600, 100, 200); | 512 DownscaleEndsAt(800, 1600, 100, 200); |
438 } | 513 } |
439 | 514 |
440 } // namespace webrtc | 515 } // namespace webrtc |
OLD | NEW |