xref: /aosp_15_r20/external/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 "modules/video_coding/utility/quality_scaler.h"
12 
13 #include <memory>
14 #include <string>
15 
16 #include "api/units/time_delta.h"
17 #include "rtc_base/checks.h"
18 #include "rtc_base/event.h"
19 #include "rtc_base/task_queue_for_test.h"
20 #include "test/field_trial.h"
21 #include "test/gtest.h"
22 
23 namespace webrtc {
24 namespace {
25 static const int kFramerate = 30;
26 static const int kLowQp = 15;
27 static const int kHighQp = 40;
28 static const int kMinFramesNeededToScale = 60;  // From quality_scaler.cc.
29 static constexpr TimeDelta kDefaultTimeout = TimeDelta::Millis(150);
30 }  // namespace
31 
32 class FakeQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
33  public:
34   ~FakeQpUsageHandler() override = default;
35 
36   // QualityScalerQpUsageHandlerInterface implementation.
OnReportQpUsageHigh()37   void OnReportQpUsageHigh() override {
38     adapt_down_events_++;
39     event.Set();
40   }
41 
OnReportQpUsageLow()42   void OnReportQpUsageLow() override {
43     adapt_up_events_++;
44     event.Set();
45   }
46 
47   rtc::Event event;
48   int adapt_up_events_ = 0;
49   int adapt_down_events_ = 0;
50 };
51 
52 // Pass a lower sampling period to speed up the tests.
53 class QualityScalerUnderTest : public QualityScaler {
54  public:
QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface * handler,VideoEncoder::QpThresholds thresholds)55   explicit QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface* handler,
56                                   VideoEncoder::QpThresholds thresholds)
57       : QualityScaler(handler, thresholds, 5) {}
58 };
59 
60 class QualityScalerTest : public ::testing::Test,
61                           public ::testing::WithParamInterface<std::string> {
62  protected:
63   enum ScaleDirection {
64     kKeepScaleAboveLowQp,
65     kKeepScaleAtHighQp,
66     kScaleDown,
67     kScaleDownAboveHighQp,
68     kScaleUp
69   };
70 
QualityScalerTest()71   QualityScalerTest()
72       : scoped_field_trial_(GetParam()),
73         task_queue_("QualityScalerTestQueue"),
74         handler_(std::make_unique<FakeQpUsageHandler>()) {
75     task_queue_.SendTask(
76         [this] {
77           qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
78               handler_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
79         });
80   }
81 
~QualityScalerTest()82   ~QualityScalerTest() override {
83     task_queue_.SendTask([this] { qs_ = nullptr; });
84   }
85 
TriggerScale(ScaleDirection scale_direction)86   void TriggerScale(ScaleDirection scale_direction) {
87     for (int i = 0; i < kFramerate * 5; ++i) {
88       switch (scale_direction) {
89         case kKeepScaleAboveLowQp:
90           qs_->ReportQp(kLowQp + 1, 0);
91           break;
92         case kScaleUp:
93           qs_->ReportQp(kLowQp, 0);
94           break;
95         case kScaleDown:
96           qs_->ReportDroppedFrameByMediaOpt();
97           break;
98         case kKeepScaleAtHighQp:
99           qs_->ReportQp(kHighQp, 0);
100           break;
101         case kScaleDownAboveHighQp:
102           qs_->ReportQp(kHighQp + 1, 0);
103           break;
104       }
105     }
106   }
107 
108   test::ScopedFieldTrials scoped_field_trial_;
109   TaskQueueForTest task_queue_;
110   std::unique_ptr<QualityScaler> qs_;
111   std::unique_ptr<FakeQpUsageHandler> handler_;
112 };
113 
114 INSTANTIATE_TEST_SUITE_P(
115     FieldTrials,
116     QualityScalerTest,
117     ::testing::Values(
118         "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.9,0.99,1/",
119         "WebRTC-Video-QualityScaling/Disabled/"));
120 
TEST_P(QualityScalerTest,DownscalesAfterContinuousFramedrop)121 TEST_P(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
122   task_queue_.SendTask([this] { TriggerScale(kScaleDown); });
123   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
124   EXPECT_EQ(1, handler_->adapt_down_events_);
125   EXPECT_EQ(0, handler_->adapt_up_events_);
126 }
127 
TEST_P(QualityScalerTest,KeepsScaleAtHighQp)128 TEST_P(QualityScalerTest, KeepsScaleAtHighQp) {
129   task_queue_.SendTask([this] { TriggerScale(kKeepScaleAtHighQp); });
130   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeout));
131   EXPECT_EQ(0, handler_->adapt_down_events_);
132   EXPECT_EQ(0, handler_->adapt_up_events_);
133 }
134 
TEST_P(QualityScalerTest,DownscalesAboveHighQp)135 TEST_P(QualityScalerTest, DownscalesAboveHighQp) {
136   task_queue_.SendTask([this] { TriggerScale(kScaleDownAboveHighQp); });
137   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
138   EXPECT_EQ(1, handler_->adapt_down_events_);
139   EXPECT_EQ(0, handler_->adapt_up_events_);
140 }
141 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsFramedrop)142 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
143   task_queue_.SendTask([this] {
144     for (int i = 0; i < kFramerate * 5; ++i) {
145       qs_->ReportDroppedFrameByMediaOpt();
146       qs_->ReportDroppedFrameByMediaOpt();
147       qs_->ReportQp(kHighQp, 0);
148     }
149   });
150   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
151   EXPECT_EQ(1, handler_->adapt_down_events_);
152   EXPECT_EQ(0, handler_->adapt_up_events_);
153 }
154 
TEST_P(QualityScalerTest,DoesNotDownscaleAfterHalfFramedrop)155 TEST_P(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
156   task_queue_.SendTask([this] {
157     for (int i = 0; i < kFramerate * 5; ++i) {
158       qs_->ReportDroppedFrameByMediaOpt();
159       qs_->ReportQp(kHighQp, 0);
160     }
161   });
162   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeout));
163   EXPECT_EQ(0, handler_->adapt_down_events_);
164   EXPECT_EQ(0, handler_->adapt_up_events_);
165 }
166 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsIfFieldTrialEnabled)167 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsIfFieldTrialEnabled) {
168   const bool kDownScaleExpected =
169       GetParam().find("Enabled") != std::string::npos;
170   task_queue_.SendTask([this] {
171     for (int i = 0; i < kFramerate * 5; ++i) {
172       qs_->ReportDroppedFrameByMediaOpt();
173       qs_->ReportDroppedFrameByEncoder();
174       qs_->ReportQp(kHighQp, 0);
175     }
176   });
177   EXPECT_EQ(kDownScaleExpected, handler_->event.Wait(kDefaultTimeout));
178   EXPECT_EQ(kDownScaleExpected ? 1 : 0, handler_->adapt_down_events_);
179   EXPECT_EQ(0, handler_->adapt_up_events_);
180 }
181 
TEST_P(QualityScalerTest,KeepsScaleOnNormalQp)182 TEST_P(QualityScalerTest, KeepsScaleOnNormalQp) {
183   task_queue_.SendTask([this] { TriggerScale(kKeepScaleAboveLowQp); });
184   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeout));
185   EXPECT_EQ(0, handler_->adapt_down_events_);
186   EXPECT_EQ(0, handler_->adapt_up_events_);
187 }
188 
TEST_P(QualityScalerTest,UpscalesAfterLowQp)189 TEST_P(QualityScalerTest, UpscalesAfterLowQp) {
190   task_queue_.SendTask([this] { TriggerScale(kScaleUp); });
191   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
192   EXPECT_EQ(0, handler_->adapt_down_events_);
193   EXPECT_EQ(1, handler_->adapt_up_events_);
194 }
195 
TEST_P(QualityScalerTest,ScalesDownAndBackUp)196 TEST_P(QualityScalerTest, ScalesDownAndBackUp) {
197   task_queue_.SendTask([this] { TriggerScale(kScaleDown); });
198   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
199   EXPECT_EQ(1, handler_->adapt_down_events_);
200   EXPECT_EQ(0, handler_->adapt_up_events_);
201   task_queue_.SendTask([this] { TriggerScale(kScaleUp); });
202   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
203   EXPECT_EQ(1, handler_->adapt_down_events_);
204   EXPECT_EQ(1, handler_->adapt_up_events_);
205 }
206 
TEST_P(QualityScalerTest,DoesNotScaleUntilEnoughFramesObserved)207 TEST_P(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
208   task_queue_.SendTask([this] {
209     // Not enough frames to make a decision.
210     for (int i = 0; i < kMinFramesNeededToScale - 1; ++i) {
211       qs_->ReportQp(kLowQp, 0);
212     }
213   });
214   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeout));
215   task_queue_.SendTask([this] {
216     // Send 1 more. Enough frames observed, should result in an adapt
217     // request.
218     qs_->ReportQp(kLowQp, 0);
219   });
220   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
221   EXPECT_EQ(0, handler_->adapt_down_events_);
222   EXPECT_EQ(1, handler_->adapt_up_events_);
223 
224   // Samples should be cleared after an adapt request.
225   task_queue_.SendTask([this] {
226     // Not enough frames to make a decision.
227     qs_->ReportQp(kLowQp, 0);
228   });
229   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeout));
230   EXPECT_EQ(0, handler_->adapt_down_events_);
231   EXPECT_EQ(1, handler_->adapt_up_events_);
232 }
233 
TEST_P(QualityScalerTest,ScalesDownAndBackUpWithMinFramesNeeded)234 TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
235   task_queue_.SendTask([this] {
236     for (int i = 0; i < kMinFramesNeededToScale; ++i) {
237       qs_->ReportQp(kHighQp + 1, 0);
238     }
239   });
240   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
241   EXPECT_EQ(1, handler_->adapt_down_events_);
242   EXPECT_EQ(0, handler_->adapt_up_events_);
243   // Samples cleared.
244   task_queue_.SendTask([this] {
245     for (int i = 0; i < kMinFramesNeededToScale; ++i) {
246       qs_->ReportQp(kLowQp, 0);
247     }
248   });
249   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeout));
250   EXPECT_EQ(1, handler_->adapt_down_events_);
251   EXPECT_EQ(1, handler_->adapt_up_events_);
252 }
253 
254 }  // namespace webrtc
255