Home | History | Annotate | Download | only in utility
      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