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