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