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 "rtc_base/checks.h"
17 #include "rtc_base/event.h"
18 #include "rtc_base/task_queue_for_test.h"
19 #include "test/field_trial.h"
20 #include "test/gtest.h"
21 
22 namespace webrtc {
23 namespace {
24 static const int kFramerate = 30;
25 static const int kLowQp = 15;
26 static const int kHighQp = 40;
27 static const int kMinFramesNeededToScale = 60;  // From quality_scaler.cc.
28 static const size_t kDefaultTimeoutMs = 150;
29 }  // namespace
30 
31 class FakeQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
32  public:
33   ~FakeQpUsageHandler() override = default;
34 
35   // QualityScalerQpUsageHandlerInterface implementation.
OnReportQpUsageHigh()36   void OnReportQpUsageHigh() override {
37     adapt_down_events_++;
38     event.Set();
39   }
40 
OnReportQpUsageLow()41   void OnReportQpUsageLow() override {
42     adapt_up_events_++;
43     event.Set();
44   }
45 
46   rtc::Event event;
47   int adapt_up_events_ = 0;
48   int adapt_down_events_ = 0;
49 };
50 
51 // Pass a lower sampling period to speed up the tests.
52 class QualityScalerUnderTest : public QualityScaler {
53  public:
QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface * handler,VideoEncoder::QpThresholds thresholds)54   explicit QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface* handler,
55                                   VideoEncoder::QpThresholds thresholds)
56       : QualityScaler(handler, thresholds, 5) {}
57 };
58 
59 class QualityScalerTest : public ::testing::Test,
60                           public ::testing::WithParamInterface<std::string> {
61  protected:
62   enum ScaleDirection {
63     kKeepScaleAboveLowQp,
64     kKeepScaleAtHighQp,
65     kScaleDown,
66     kScaleDownAboveHighQp,
67     kScaleUp
68   };
69 
QualityScalerTest()70   QualityScalerTest()
71       : scoped_field_trial_(GetParam()),
72         task_queue_("QualityScalerTestQueue"),
73         handler_(std::make_unique<FakeQpUsageHandler>()) {
74     task_queue_.SendTask(
75         [this] {
76           qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
77               handler_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
78         },
79         RTC_FROM_HERE);
80   }
81 
~QualityScalerTest()82   ~QualityScalerTest() override {
83     task_queue_.SendTask([this] { qs_ = nullptr; }, RTC_FROM_HERE);
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         ""));
120 
TEST_P(QualityScalerTest,DownscalesAfterContinuousFramedrop)121 TEST_P(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
122   task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
123   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
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                        RTC_FROM_HERE);
131   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
132   EXPECT_EQ(0, handler_->adapt_down_events_);
133   EXPECT_EQ(0, handler_->adapt_up_events_);
134 }
135 
TEST_P(QualityScalerTest,DownscalesAboveHighQp)136 TEST_P(QualityScalerTest, DownscalesAboveHighQp) {
137   task_queue_.SendTask([this] { TriggerScale(kScaleDownAboveHighQp); },
138                        RTC_FROM_HERE);
139   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
140   EXPECT_EQ(1, handler_->adapt_down_events_);
141   EXPECT_EQ(0, handler_->adapt_up_events_);
142 }
143 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsFramedrop)144 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
145   task_queue_.SendTask(
146       [this] {
147         for (int i = 0; i < kFramerate * 5; ++i) {
148           qs_->ReportDroppedFrameByMediaOpt();
149           qs_->ReportDroppedFrameByMediaOpt();
150           qs_->ReportQp(kHighQp, 0);
151         }
152       },
153       RTC_FROM_HERE);
154   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
155   EXPECT_EQ(1, handler_->adapt_down_events_);
156   EXPECT_EQ(0, handler_->adapt_up_events_);
157 }
158 
TEST_P(QualityScalerTest,DoesNotDownscaleAfterHalfFramedrop)159 TEST_P(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
160   task_queue_.SendTask(
161       [this] {
162         for (int i = 0; i < kFramerate * 5; ++i) {
163           qs_->ReportDroppedFrameByMediaOpt();
164           qs_->ReportQp(kHighQp, 0);
165         }
166       },
167       RTC_FROM_HERE);
168   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
169   EXPECT_EQ(0, handler_->adapt_down_events_);
170   EXPECT_EQ(0, handler_->adapt_up_events_);
171 }
172 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsIfFieldTrialEnabled)173 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsIfFieldTrialEnabled) {
174   const bool kDownScaleExpected = !GetParam().empty();
175   task_queue_.SendTask(
176       [this] {
177         for (int i = 0; i < kFramerate * 5; ++i) {
178           qs_->ReportDroppedFrameByMediaOpt();
179           qs_->ReportDroppedFrameByEncoder();
180           qs_->ReportQp(kHighQp, 0);
181         }
182       },
183       RTC_FROM_HERE);
184   EXPECT_EQ(kDownScaleExpected, handler_->event.Wait(kDefaultTimeoutMs));
185   EXPECT_EQ(kDownScaleExpected ? 1 : 0, handler_->adapt_down_events_);
186   EXPECT_EQ(0, handler_->adapt_up_events_);
187 }
188 
TEST_P(QualityScalerTest,KeepsScaleOnNormalQp)189 TEST_P(QualityScalerTest, KeepsScaleOnNormalQp) {
190   task_queue_.SendTask([this] { TriggerScale(kKeepScaleAboveLowQp); },
191                        RTC_FROM_HERE);
192   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
193   EXPECT_EQ(0, handler_->adapt_down_events_);
194   EXPECT_EQ(0, handler_->adapt_up_events_);
195 }
196 
TEST_P(QualityScalerTest,UpscalesAfterLowQp)197 TEST_P(QualityScalerTest, UpscalesAfterLowQp) {
198   task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
199   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
200   EXPECT_EQ(0, handler_->adapt_down_events_);
201   EXPECT_EQ(1, handler_->adapt_up_events_);
202 }
203 
TEST_P(QualityScalerTest,ScalesDownAndBackUp)204 TEST_P(QualityScalerTest, ScalesDownAndBackUp) {
205   task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
206   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
207   EXPECT_EQ(1, handler_->adapt_down_events_);
208   EXPECT_EQ(0, handler_->adapt_up_events_);
209   task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
210   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
211   EXPECT_EQ(1, handler_->adapt_down_events_);
212   EXPECT_EQ(1, handler_->adapt_up_events_);
213 }
214 
TEST_P(QualityScalerTest,DoesNotScaleUntilEnoughFramesObserved)215 TEST_P(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
216   task_queue_.SendTask(
217       [this] {
218         // Not enough frames to make a decision.
219         for (int i = 0; i < kMinFramesNeededToScale - 1; ++i) {
220           qs_->ReportQp(kLowQp, 0);
221         }
222       },
223       RTC_FROM_HERE);
224   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
225   task_queue_.SendTask(
226       [this] {
227         // Send 1 more. Enough frames observed, should result in an adapt
228         // request.
229         qs_->ReportQp(kLowQp, 0);
230       },
231       RTC_FROM_HERE);
232   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
233   EXPECT_EQ(0, handler_->adapt_down_events_);
234   EXPECT_EQ(1, handler_->adapt_up_events_);
235 
236   // Samples should be cleared after an adapt request.
237   task_queue_.SendTask(
238       [this] {
239         // Not enough frames to make a decision.
240         qs_->ReportQp(kLowQp, 0);
241       },
242       RTC_FROM_HERE);
243   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
244   EXPECT_EQ(0, handler_->adapt_down_events_);
245   EXPECT_EQ(1, handler_->adapt_up_events_);
246 }
247 
TEST_P(QualityScalerTest,ScalesDownAndBackUpWithMinFramesNeeded)248 TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
249   task_queue_.SendTask(
250       [this] {
251         for (int i = 0; i < kMinFramesNeededToScale; ++i) {
252           qs_->ReportQp(kHighQp + 1, 0);
253         }
254       },
255       RTC_FROM_HERE);
256   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
257   EXPECT_EQ(1, handler_->adapt_down_events_);
258   EXPECT_EQ(0, handler_->adapt_up_events_);
259   // Samples cleared.
260   task_queue_.SendTask(
261       [this] {
262         for (int i = 0; i < kMinFramesNeededToScale; ++i) {
263           qs_->ReportQp(kLowQp, 0);
264         }
265       },
266       RTC_FROM_HERE);
267   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
268   EXPECT_EQ(1, handler_->adapt_down_events_);
269   EXPECT_EQ(1, handler_->adapt_up_events_);
270 }
271 
272 }  // namespace webrtc
273