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; | |
35 } // namespace | 30 } // namespace |
36 | 31 |
37 class QualityScalerTest : public ::testing::Test { | 32 class QualityScalerTest : public ::testing::Test { |
38 public: | 33 public: |
39 // Temporal and spatial resolution. | 34 // Temporal and spatial resolution. |
40 struct Resolution { | 35 struct Resolution { |
41 int framerate; | 36 int framerate; |
42 int width; | 37 int width; |
43 int height; | 38 int height; |
44 }; | 39 }; |
45 | 40 |
46 protected: | 41 protected: |
47 enum ScaleDirection { | 42 enum ScaleDirection { |
48 kKeepScaleAtHighQp, | 43 kKeepScaleAtHighQp, |
49 kScaleDown, | 44 kScaleDown, |
50 kScaleDownAboveHighQp, | 45 kScaleDownAboveHighQp, |
51 kScaleUp | 46 kScaleUp |
52 }; | 47 }; |
53 enum BadQualityMetric { kDropFrame, kReportLowQP }; | 48 enum BadQualityMetric { kDropFrame, kReportLowQP }; |
54 | 49 |
55 QualityScalerTest() { | 50 QualityScalerTest() { |
56 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, | 51 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, |
57 kHalfWidth); | 52 kHalfWidth); |
58 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, | 53 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false, |
59 0, 0, 0, kFramerate); | 54 0, 0, 0); |
| 55 qs_.ReportFramerate(kFramerate); |
60 qs_.OnEncodeFrame(input_frame_); | 56 qs_.OnEncodeFrame(input_frame_); |
61 } | 57 } |
62 | 58 |
63 bool TriggerScale(ScaleDirection scale_direction) { | 59 bool TriggerScale(ScaleDirection scale_direction) { |
64 qs_.OnEncodeFrame(input_frame_); | 60 qs_.OnEncodeFrame(input_frame_); |
65 int initial_width = qs_.GetScaledResolution().width; | 61 int initial_width = qs_.GetScaledResolution().width; |
66 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 62 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
67 switch (scale_direction) { | 63 switch (scale_direction) { |
68 case kScaleUp: | 64 case kScaleUp: |
69 qs_.ReportQP(kLowQp); | 65 qs_.ReportQP(kLowQp); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 | 97 |
102 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 98 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
103 | 99 |
104 void DoesNotDownscaleFrameDimensions(int width, int height); | 100 void DoesNotDownscaleFrameDimensions(int width, int height); |
105 | 101 |
106 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, | 102 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, |
107 int num_second, | 103 int num_second, |
108 int initial_framerate); | 104 int initial_framerate); |
109 | 105 |
110 void VerifyQualityAdaptation(int initial_framerate, | 106 void VerifyQualityAdaptation(int initial_framerate, |
111 int seconds_downscale, | 107 int seconds, |
112 int seconds_upscale, | |
113 bool expect_spatial_resize, | 108 bool expect_spatial_resize, |
114 bool expect_framerate_reduction); | 109 bool expect_framerate_reduction); |
115 | 110 |
116 void DownscaleEndsAt(int input_width, | 111 void DownscaleEndsAt(int input_width, |
117 int input_height, | 112 int input_height, |
118 int end_width, | 113 int end_width, |
119 int end_height); | 114 int end_height); |
120 | 115 |
121 QualityScaler qs_; | 116 QualityScaler qs_; |
122 VideoFrame input_frame_; | 117 VideoFrame input_frame_; |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 if (res.framerate != -1) | 291 if (res.framerate != -1) |
297 qs_.ReportFramerate(res.framerate); | 292 qs_.ReportFramerate(res.framerate); |
298 res.width = qs_.GetScaledResolution().width; | 293 res.width = qs_.GetScaledResolution().width; |
299 res.height = qs_.GetScaledResolution().height; | 294 res.height = qs_.GetScaledResolution().height; |
300 } | 295 } |
301 return res; | 296 return res; |
302 } | 297 } |
303 | 298 |
304 void QualityScalerTest::VerifyQualityAdaptation( | 299 void QualityScalerTest::VerifyQualityAdaptation( |
305 int initial_framerate, | 300 int initial_framerate, |
306 int seconds_downscale, | 301 int seconds, |
307 int seconds_upscale, | |
308 bool expect_spatial_resize, | 302 bool expect_spatial_resize, |
309 bool expect_framerate_reduction) { | 303 bool expect_framerate_reduction) { |
310 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 304 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
311 kDisabledBadQpThreshold, true, 0, 0, 0, initial_framerate); | 305 kDisabledBadQpThreshold, true, 0, 0, 0); |
312 qs_.OnEncodeFrame(input_frame_); | 306 qs_.OnEncodeFrame(input_frame_); |
313 int init_width = qs_.GetScaledResolution().width; | 307 int init_width = qs_.GetScaledResolution().width; |
314 int init_height = qs_.GetScaledResolution().height; | 308 int init_height = qs_.GetScaledResolution().height; |
315 | 309 |
316 // Test reducing framerate by dropping frame continuously. | 310 // Test reducing framerate by dropping frame continuously. |
317 QualityScalerTest::Resolution res = | 311 QualityScalerTest::Resolution res = |
318 TriggerResolutionChange(kDropFrame, seconds_downscale, initial_framerate); | 312 TriggerResolutionChange(kDropFrame, seconds, initial_framerate); |
319 | 313 |
320 if (expect_framerate_reduction) { | 314 if (expect_framerate_reduction) { |
321 EXPECT_LT(res.framerate, initial_framerate); | 315 EXPECT_LT(res.framerate, initial_framerate); |
322 } else { | 316 } else { |
323 // No framerate reduction, video decimator should be disabled. | 317 // No framerate reduction, video decimator should be disabled. |
324 EXPECT_EQ(-1, res.framerate); | 318 EXPECT_EQ(-1, res.framerate); |
325 } | 319 } |
326 | 320 |
327 if (expect_spatial_resize) { | 321 if (expect_spatial_resize) { |
328 EXPECT_LT(res.width, init_width); | 322 EXPECT_LT(res.width, init_width); |
329 EXPECT_LT(res.height, init_height); | 323 EXPECT_LT(res.height, init_height); |
330 } else { | 324 } else { |
331 EXPECT_EQ(init_width, res.width); | 325 EXPECT_EQ(init_width, res.width); |
332 EXPECT_EQ(init_height, res.height); | 326 EXPECT_EQ(init_height, res.height); |
333 } | 327 } |
334 | 328 |
335 // The "seconds * 1.5" is to ensure spatial resolution to recover. | 329 // The "seconds * 1.5" is to ensure spatial resolution to recover. |
336 // For example, in 6 seconds test, framerate reduction happens in the first | 330 // For example, in 10 seconds test, framerate reduction happens in the first |
337 // 3 seconds from 30fps to 15fps and causes the buffer size to be half of the | 331 // 5 seconds from 30fps to 15fps and causes the buffer size to be half of the |
338 // original one. Then it will take only 45 samples to downscale (twice in 90 | 332 // original one. Then it will take only 75 samples to downscale (twice in 150 |
339 // samples). So to recover the resolution changes, we need more than 10 | 333 // samples). So to recover the resolution changes, we need more than 10 |
340 // seconds (i.e, seconds_upscale * 1.5). This is because the framerate | 334 // seconds (i.e, seconds * 1.5). This is because the framerate increases |
341 // increases before spatial size recovers, so it will take 150 samples to | 335 // before spatial size recovers, so it will take 150 samples to recover |
342 // recover spatial size (300 for twice). | 336 // spatial size (300 for twice). |
343 res = TriggerResolutionChange(kReportLowQP, seconds_upscale * 1.5, | 337 res = TriggerResolutionChange(kReportLowQP, seconds * 1.5, initial_framerate); |
344 initial_framerate); | |
345 EXPECT_EQ(-1, res.framerate); | 338 EXPECT_EQ(-1, res.framerate); |
346 EXPECT_EQ(init_width, res.width); | 339 EXPECT_EQ(init_width, res.width); |
347 EXPECT_EQ(init_height, res.height); | 340 EXPECT_EQ(init_height, res.height); |
348 } | 341 } |
349 | 342 |
350 // In 3 seconds test, only framerate adjusting should happen and 5 second | 343 // In 5 seconds test, only framerate adjusting should happen. |
351 // upscaling duration, only a framerate adjusting should happen. | |
352 TEST_F(QualityScalerTest, ChangeFramerateOnly) { | 344 TEST_F(QualityScalerTest, ChangeFramerateOnly) { |
353 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale, | 345 VerifyQualityAdaptation(kFramerate, 5, false, true); |
354 kMeasureSecondsUpscale, false, true); | |
355 } | 346 } |
356 | 347 |
357 // In 6 seconds test, framerate adjusting and scaling are both | 348 // In 10 seconds test, framerate adjusting and scaling are both |
358 // triggered, it shows that scaling would happen after framerate | 349 // triggered, it shows that scaling would happen after framerate |
359 // adjusting. | 350 // adjusting. |
360 TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) { | 351 TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) { |
361 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale * 2, | 352 VerifyQualityAdaptation(kFramerate, 10, true, true); |
362 kMeasureSecondsUpscale * 2, true, true); | |
363 } | 353 } |
364 | 354 |
365 // When starting from a low framerate, only spatial size will be changed. | 355 // When starting from a low framerate, only spatial size will be changed. |
366 TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) { | 356 TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) { |
367 qs_.ReportFramerate(kFramerate >> 1); | 357 qs_.ReportFramerate(kFramerate >> 1); |
368 VerifyQualityAdaptation(kFramerate >> 1, kMeasureSecondsDownscale * 2, | 358 VerifyQualityAdaptation(kFramerate >> 1, 10, true, false); |
369 kMeasureSecondsUpscale * 2, true, false); | |
370 } | 359 } |
371 | 360 |
372 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { | 361 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { |
373 DoesNotDownscaleFrameDimensions( | 362 DoesNotDownscaleFrameDimensions( |
374 2 * QualityScaler::kDefaultMinDownscaleDimension - 1, 1000); | 363 2 * QualityScaler::kDefaultMinDownscaleDimension - 1, 1000); |
375 } | 364 } |
376 | 365 |
377 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { | 366 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { |
378 DoesNotDownscaleFrameDimensions( | 367 DoesNotDownscaleFrameDimensions( |
379 1000, 2 * QualityScaler::kDefaultMinDownscaleDimension - 1); | 368 1000, 2 * QualityScaler::kDefaultMinDownscaleDimension - 1); |
380 } | 369 } |
381 | 370 |
382 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { | 371 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { |
383 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 372 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
384 kDisabledBadQpThreshold, true, | 373 kDisabledBadQpThreshold, true, |
385 kLowInitialBitrateKbps, kWidth, kHeight, kFramerate); | 374 kLowInitialBitrateKbps, kWidth, kHeight); |
386 qs_.OnEncodeFrame(input_frame_); | 375 qs_.OnEncodeFrame(input_frame_); |
387 int init_width = qs_.GetScaledResolution().width; | 376 int init_width = qs_.GetScaledResolution().width; |
388 int init_height = qs_.GetScaledResolution().height; | 377 int init_height = qs_.GetScaledResolution().height; |
389 EXPECT_LE(init_width, kWidthVga); | 378 EXPECT_LE(init_width, kWidthVga); |
390 EXPECT_LE(init_height, kHeightVga); | 379 EXPECT_LE(init_height, kHeightVga); |
391 } | 380 } |
392 | 381 |
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 | |
457 void QualityScalerTest::DownscaleEndsAt(int input_width, | 382 void QualityScalerTest::DownscaleEndsAt(int input_width, |
458 int input_height, | 383 int input_height, |
459 int end_width, | 384 int end_width, |
460 int end_height) { | 385 int end_height) { |
461 // Create a frame with 2x expected end width/height to verify that we can | 386 // Create a frame with 2x expected end width/height to verify that we can |
462 // scale down to expected end width/height. | 387 // scale down to expected end width/height. |
463 input_frame_.CreateEmptyFrame(input_width, input_height, input_width, | 388 input_frame_.CreateEmptyFrame(input_width, input_height, input_width, |
464 (input_width + 1) / 2, (input_width + 1) / 2); | 389 (input_width + 1) / 2, (input_width + 1) / 2); |
465 | 390 |
466 int last_width = input_width; | 391 int last_width = input_width; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 DownscaleEndsAt(1600, 800, 200, 100); | 431 DownscaleEndsAt(1600, 800, 200, 100); |
507 } | 432 } |
508 | 433 |
509 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { | 434 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { |
510 // Should end at 100x200, as height can't go lower. | 435 // Should end at 100x200, as height can't go lower. |
511 qs_.SetMinResolution(10, 200); | 436 qs_.SetMinResolution(10, 200); |
512 DownscaleEndsAt(800, 1600, 100, 200); | 437 DownscaleEndsAt(800, 1600, 100, 200); |
513 } | 438 } |
514 | 439 |
515 } // namespace webrtc | 440 } // namespace webrtc |
OLD | NEW |