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 MockQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
32  public:
~MockQpUsageHandler()33   virtual ~MockQpUsageHandler() {}
34 
35   // QualityScalerQpUsageHandlerInterface implementation.
OnReportQpUsageHigh(rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)36   void OnReportQpUsageHigh(
37       rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)
38       override {
39     callback_ = callback;
40     adapt_down_events_++;
41     event.Set();
42     if (synchronously_invoke_callback)
43       callback_->OnQpUsageHandled(true);
44   }
45 
OnReportQpUsageLow(rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)46   void OnReportQpUsageLow(
47       rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)
48       override {
49     callback_ = callback;
50     adapt_up_events_++;
51     event.Set();
52     if (synchronously_invoke_callback)
53       callback_->OnQpUsageHandled(true);
54   }
55 
56   rtc::Event event;
57   int adapt_up_events_ = 0;
58   int adapt_down_events_ = 0;
59   bool synchronously_invoke_callback = true;
60   rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback_ =
61       nullptr;
62 };
63 
64 // Pass a lower sampling period to speed up the tests.
65 class QualityScalerUnderTest : public QualityScaler {
66  public:
QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface * handler,VideoEncoder::QpThresholds thresholds)67   explicit QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface* handler,
68                                   VideoEncoder::QpThresholds thresholds)
69       : QualityScaler(handler, thresholds, 5) {}
70 };
71 
72 class QualityScalerTest : public ::testing::Test,
73                           public ::testing::WithParamInterface<std::string> {
74  protected:
75   enum ScaleDirection {
76     kKeepScaleAboveLowQp,
77     kKeepScaleAtHighQp,
78     kScaleDown,
79     kScaleDownAboveHighQp,
80     kScaleUp
81   };
82 
QualityScalerTest()83   QualityScalerTest()
84       : scoped_field_trial_(GetParam()),
85         task_queue_("QualityScalerTestQueue"),
86         handler_(new MockQpUsageHandler()) {
87     task_queue_.SendTask(
88         [this] {
89           qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
90               handler_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
91         },
92         RTC_FROM_HERE);
93   }
94 
~QualityScalerTest()95   ~QualityScalerTest() {
96     task_queue_.SendTask([this] { qs_ = nullptr; }, RTC_FROM_HERE);
97   }
98 
TriggerScale(ScaleDirection scale_direction)99   void TriggerScale(ScaleDirection scale_direction) {
100     for (int i = 0; i < kFramerate * 5; ++i) {
101       switch (scale_direction) {
102         case kKeepScaleAboveLowQp:
103           qs_->ReportQp(kLowQp + 1, 0);
104           break;
105         case kScaleUp:
106           qs_->ReportQp(kLowQp, 0);
107           break;
108         case kScaleDown:
109           qs_->ReportDroppedFrameByMediaOpt();
110           break;
111         case kKeepScaleAtHighQp:
112           qs_->ReportQp(kHighQp, 0);
113           break;
114         case kScaleDownAboveHighQp:
115           qs_->ReportQp(kHighQp + 1, 0);
116           break;
117       }
118     }
119   }
120 
121   test::ScopedFieldTrials scoped_field_trial_;
122   TaskQueueForTest task_queue_;
123   std::unique_ptr<QualityScaler> qs_;
124   std::unique_ptr<MockQpUsageHandler> handler_;
125 };
126 
127 INSTANTIATE_TEST_SUITE_P(
128     FieldTrials,
129     QualityScalerTest,
130     ::testing::Values(
131         "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.9,0.99,1/",
132         ""));
133 
TEST_P(QualityScalerTest,DownscalesAfterContinuousFramedrop)134 TEST_P(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
135   task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
136   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
137   EXPECT_EQ(1, handler_->adapt_down_events_);
138   EXPECT_EQ(0, handler_->adapt_up_events_);
139 }
140 
TEST_P(QualityScalerTest,KeepsScaleAtHighQp)141 TEST_P(QualityScalerTest, KeepsScaleAtHighQp) {
142   task_queue_.SendTask([this] { TriggerScale(kKeepScaleAtHighQp); },
143                        RTC_FROM_HERE);
144   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
145   EXPECT_EQ(0, handler_->adapt_down_events_);
146   EXPECT_EQ(0, handler_->adapt_up_events_);
147 }
148 
TEST_P(QualityScalerTest,DownscalesAboveHighQp)149 TEST_P(QualityScalerTest, DownscalesAboveHighQp) {
150   task_queue_.SendTask([this] { TriggerScale(kScaleDownAboveHighQp); },
151                        RTC_FROM_HERE);
152   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
153   EXPECT_EQ(1, handler_->adapt_down_events_);
154   EXPECT_EQ(0, handler_->adapt_up_events_);
155 }
156 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsFramedrop)157 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
158   task_queue_.SendTask(
159       [this] {
160         for (int i = 0; i < kFramerate * 5; ++i) {
161           qs_->ReportDroppedFrameByMediaOpt();
162           qs_->ReportDroppedFrameByMediaOpt();
163           qs_->ReportQp(kHighQp, 0);
164         }
165       },
166       RTC_FROM_HERE);
167   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
168   EXPECT_EQ(1, handler_->adapt_down_events_);
169   EXPECT_EQ(0, handler_->adapt_up_events_);
170 }
171 
TEST_P(QualityScalerTest,DoesNotDownscaleAfterHalfFramedrop)172 TEST_P(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
173   task_queue_.SendTask(
174       [this] {
175         for (int i = 0; i < kFramerate * 5; ++i) {
176           qs_->ReportDroppedFrameByMediaOpt();
177           qs_->ReportQp(kHighQp, 0);
178         }
179       },
180       RTC_FROM_HERE);
181   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
182   EXPECT_EQ(0, handler_->adapt_down_events_);
183   EXPECT_EQ(0, handler_->adapt_up_events_);
184 }
185 
TEST_P(QualityScalerTest,DownscalesAfterTwoThirdsIfFieldTrialEnabled)186 TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsIfFieldTrialEnabled) {
187   const bool kDownScaleExpected = !GetParam().empty();
188   task_queue_.SendTask(
189       [this] {
190         for (int i = 0; i < kFramerate * 5; ++i) {
191           qs_->ReportDroppedFrameByMediaOpt();
192           qs_->ReportDroppedFrameByEncoder();
193           qs_->ReportQp(kHighQp, 0);
194         }
195       },
196       RTC_FROM_HERE);
197   EXPECT_EQ(kDownScaleExpected, handler_->event.Wait(kDefaultTimeoutMs));
198   EXPECT_EQ(kDownScaleExpected ? 1 : 0, handler_->adapt_down_events_);
199   EXPECT_EQ(0, handler_->adapt_up_events_);
200 }
201 
TEST_P(QualityScalerTest,KeepsScaleOnNormalQp)202 TEST_P(QualityScalerTest, KeepsScaleOnNormalQp) {
203   task_queue_.SendTask([this] { TriggerScale(kKeepScaleAboveLowQp); },
204                        RTC_FROM_HERE);
205   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
206   EXPECT_EQ(0, handler_->adapt_down_events_);
207   EXPECT_EQ(0, handler_->adapt_up_events_);
208 }
209 
TEST_P(QualityScalerTest,UpscalesAfterLowQp)210 TEST_P(QualityScalerTest, UpscalesAfterLowQp) {
211   task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
212   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
213   EXPECT_EQ(0, handler_->adapt_down_events_);
214   EXPECT_EQ(1, handler_->adapt_up_events_);
215 }
216 
TEST_P(QualityScalerTest,ScalesDownAndBackUp)217 TEST_P(QualityScalerTest, ScalesDownAndBackUp) {
218   task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
219   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
220   EXPECT_EQ(1, handler_->adapt_down_events_);
221   EXPECT_EQ(0, handler_->adapt_up_events_);
222   task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
223   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
224   EXPECT_EQ(1, handler_->adapt_down_events_);
225   EXPECT_EQ(1, handler_->adapt_up_events_);
226 }
227 
TEST_P(QualityScalerTest,DoesNotScaleUntilEnoughFramesObserved)228 TEST_P(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
229   task_queue_.SendTask(
230       [this] {
231         // Not enough frames to make a decision.
232         for (int i = 0; i < kMinFramesNeededToScale - 1; ++i) {
233           qs_->ReportQp(kLowQp, 0);
234         }
235       },
236       RTC_FROM_HERE);
237   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
238   task_queue_.SendTask(
239       [this] {
240         // Send 1 more. Enough frames observed, should result in an adapt
241         // request.
242         qs_->ReportQp(kLowQp, 0);
243       },
244       RTC_FROM_HERE);
245   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
246   EXPECT_EQ(0, handler_->adapt_down_events_);
247   EXPECT_EQ(1, handler_->adapt_up_events_);
248 
249   // Samples should be cleared after an adapt request.
250   task_queue_.SendTask(
251       [this] {
252         // Not enough frames to make a decision.
253         qs_->ReportQp(kLowQp, 0);
254       },
255       RTC_FROM_HERE);
256   EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
257   EXPECT_EQ(0, handler_->adapt_down_events_);
258   EXPECT_EQ(1, handler_->adapt_up_events_);
259 }
260 
TEST_P(QualityScalerTest,ScalesDownAndBackUpWithMinFramesNeeded)261 TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
262   task_queue_.SendTask(
263       [this] {
264         for (int i = 0; i < kMinFramesNeededToScale; ++i) {
265           qs_->ReportQp(kHighQp + 1, 0);
266         }
267       },
268       RTC_FROM_HERE);
269   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
270   EXPECT_EQ(1, handler_->adapt_down_events_);
271   EXPECT_EQ(0, handler_->adapt_up_events_);
272   // Samples cleared.
273   task_queue_.SendTask(
274       [this] {
275         for (int i = 0; i < kMinFramesNeededToScale; ++i) {
276           qs_->ReportQp(kLowQp, 0);
277         }
278       },
279       RTC_FROM_HERE);
280   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
281   EXPECT_EQ(1, handler_->adapt_down_events_);
282   EXPECT_EQ(1, handler_->adapt_up_events_);
283 }
284 
TEST_P(QualityScalerTest,CheckingQpAgainRequiresResolvingCallback)285 TEST_P(QualityScalerTest, CheckingQpAgainRequiresResolvingCallback) {
286   handler_->synchronously_invoke_callback = false;
287   task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
288   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
289   EXPECT_EQ(1, handler_->adapt_down_events_);
290   // Without invoking the callback, another downscale should not happen.
291   handler_->event.Reset();
292   rtc::Event event;
293   task_queue_.SendTask(
294       [this, &event] {
295         TriggerScale(kScaleDown);
296         event.Set();
297       },
298       RTC_FROM_HERE);
299   EXPECT_TRUE(event.Wait(kDefaultTimeoutMs));
300   EXPECT_FALSE(handler_->event.Wait(0));
301   EXPECT_EQ(1, handler_->adapt_down_events_);
302   // Resume checking for QP again by invoking the callback.
303   task_queue_.SendTask(
304       [this] {
305         handler_->callback_->OnQpUsageHandled(true);
306         TriggerScale(kScaleDown);
307       },
308       RTC_FROM_HERE);
309   EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
310   EXPECT_EQ(2, handler_->adapt_down_events_);
311   task_queue_.SendTask([this] { handler_->callback_->OnQpUsageHandled(true); },
312                        RTC_FROM_HERE);
313 }
314 
315 }  // namespace webrtc
316