blob: 275b327960179d7fe64bb34de753e838140d1442 [file] [log] [blame]
pbos@webrtc.orga0d78272014-09-12 11:51:47 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/utility/quality_scaler.h"
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000012
kthelgason876222f2016-11-29 01:44:11 -080013#include <memory>
Åsa Perssona945aee2018-04-24 16:53:25 +020014#include <string>
kthelgason876222f2016-11-29 01:44:11 -080015
Yves Gerey3e707812018-11-28 16:47:49 +010016#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/event.h"
Danil Chapovalovb42165e2019-03-20 14:29:43 +010018#include "rtc_base/task_queue_for_test.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020019#include "test/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "test/gtest.h"
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000021
22namespace webrtc {
23namespace {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000024static const int kFramerate = 30;
25static const int kLowQp = 15;
Peter Boström17417702015-09-25 17:03:26 +020026static const int kHighQp = 40;
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020027static const int kMinFramesNeededToScale = 60; // From quality_scaler.cc.
kthelgason86cf9a22016-12-01 02:57:01 -080028static const size_t kDefaultTimeoutMs = 150;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000029} // namespace
30
Henrik Boström012aa372020-04-27 17:40:55 +020031class MockQpUsageHandler : public QualityScalerQpUsageHandlerInterface {
kthelgason876222f2016-11-29 01:44:11 -080032 public:
Henrik Boström012aa372020-04-27 17:40:55 +020033 virtual ~MockQpUsageHandler() {}
kthelgason876222f2016-11-29 01:44:11 -080034
Henrik Boström012aa372020-04-27 17:40:55 +020035 // QualityScalerQpUsageHandlerInterface implementation.
36 void OnReportQpUsageHigh(
37 rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback)
38 override {
39 callback_ = callback;
sprangb1ca0732017-02-01 08:38:12 -080040 adapt_down_events_++;
kthelgason876222f2016-11-29 01:44:11 -080041 event.Set();
Henrik Boström012aa372020-04-27 17:40:55 +020042 if (synchronously_invoke_callback)
43 callback_->OnQpUsageHandled(true);
44 }
45
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);
kthelgason876222f2016-11-29 01:44:11 -080054 }
55
56 rtc::Event event;
sprangb1ca0732017-02-01 08:38:12 -080057 int adapt_up_events_ = 0;
58 int adapt_down_events_ = 0;
Henrik Boström012aa372020-04-27 17:40:55 +020059 bool synchronously_invoke_callback = true;
60 rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> callback_ =
61 nullptr;
kthelgason876222f2016-11-29 01:44:11 -080062};
63
64// Pass a lower sampling period to speed up the tests.
65class QualityScalerUnderTest : public QualityScaler {
66 public:
Henrik Boström012aa372020-04-27 17:40:55 +020067 explicit QualityScalerUnderTest(QualityScalerQpUsageHandlerInterface* handler,
kthelgason876222f2016-11-29 01:44:11 -080068 VideoEncoder::QpThresholds thresholds)
Henrik Boström012aa372020-04-27 17:40:55 +020069 : QualityScaler(handler, thresholds, 5) {}
kthelgason876222f2016-11-29 01:44:11 -080070};
71
Åsa Perssona945aee2018-04-24 16:53:25 +020072class QualityScalerTest : public ::testing::Test,
73 public ::testing::WithParamInterface<std::string> {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000074 protected:
Peter Boström17417702015-09-25 17:03:26 +020075 enum ScaleDirection {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020076 kKeepScaleAboveLowQp,
Peter Boström17417702015-09-25 17:03:26 +020077 kKeepScaleAtHighQp,
78 kScaleDown,
79 kScaleDownAboveHighQp,
80 kScaleUp
81 };
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000082
kthelgason876222f2016-11-29 01:44:11 -080083 QualityScalerTest()
Åsa Perssona945aee2018-04-24 16:53:25 +020084 : scoped_field_trial_(GetParam()),
Danil Chapovalovb42165e2019-03-20 14:29:43 +010085 task_queue_("QualityScalerTestQueue"),
Henrik Boström012aa372020-04-27 17:40:55 +020086 handler_(new MockQpUsageHandler()) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +020087 task_queue_.SendTask(
88 [this] {
89 qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
Henrik Boström012aa372020-04-27 17:40:55 +020090 handler_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +020091 },
92 RTC_FROM_HERE);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000093 }
94
kthelgason876222f2016-11-29 01:44:11 -080095 ~QualityScalerTest() {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +020096 task_queue_.SendTask([this] { qs_ = nullptr; }, RTC_FROM_HERE);
kthelgason876222f2016-11-29 01:44:11 -080097 }
98
99 void TriggerScale(ScaleDirection scale_direction) {
100 for (int i = 0; i < kFramerate * 5; ++i) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000101 switch (scale_direction) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200102 case kKeepScaleAboveLowQp:
Sebastian Janssonb6789402019-03-01 15:40:49 +0100103 qs_->ReportQp(kLowQp + 1, 0);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200104 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000105 case kScaleUp:
Sebastian Janssonb6789402019-03-01 15:40:49 +0100106 qs_->ReportQp(kLowQp, 0);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000107 break;
108 case kScaleDown:
Åsa Perssona945aee2018-04-24 16:53:25 +0200109 qs_->ReportDroppedFrameByMediaOpt();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000110 break;
Peter Boström17417702015-09-25 17:03:26 +0200111 case kKeepScaleAtHighQp:
Sebastian Janssonb6789402019-03-01 15:40:49 +0100112 qs_->ReportQp(kHighQp, 0);
Peter Boström17417702015-09-25 17:03:26 +0200113 break;
114 case kScaleDownAboveHighQp:
Sebastian Janssonb6789402019-03-01 15:40:49 +0100115 qs_->ReportQp(kHighQp + 1, 0);
Peter Boström17417702015-09-25 17:03:26 +0200116 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000117 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000118 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000119 }
120
Åsa Perssona945aee2018-04-24 16:53:25 +0200121 test::ScopedFieldTrials scoped_field_trial_;
Danil Chapovalovb42165e2019-03-20 14:29:43 +0100122 TaskQueueForTest task_queue_;
kthelgason876222f2016-11-29 01:44:11 -0800123 std::unique_ptr<QualityScaler> qs_;
Henrik Boström012aa372020-04-27 17:40:55 +0200124 std::unique_ptr<MockQpUsageHandler> handler_;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000125};
126
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100127INSTANTIATE_TEST_SUITE_P(
Åsa Perssona945aee2018-04-24 16:53:25 +0200128 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
134TEST_P(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200135 task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200136 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
137 EXPECT_EQ(1, handler_->adapt_down_events_);
138 EXPECT_EQ(0, handler_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000139}
140
Åsa Perssona945aee2018-04-24 16:53:25 +0200141TEST_P(QualityScalerTest, KeepsScaleAtHighQp) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200142 task_queue_.SendTask([this] { TriggerScale(kKeepScaleAtHighQp); },
143 RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200144 EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
145 EXPECT_EQ(0, handler_->adapt_down_events_);
146 EXPECT_EQ(0, handler_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200147}
148
Åsa Perssona945aee2018-04-24 16:53:25 +0200149TEST_P(QualityScalerTest, DownscalesAboveHighQp) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200150 task_queue_.SendTask([this] { TriggerScale(kScaleDownAboveHighQp); },
151 RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200152 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
153 EXPECT_EQ(1, handler_->adapt_down_events_);
154 EXPECT_EQ(0, handler_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200155}
156
Åsa Perssona945aee2018-04-24 16:53:25 +0200157TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200158 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200167 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
168 EXPECT_EQ(1, handler_->adapt_down_events_);
169 EXPECT_EQ(0, handler_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000170}
171
Åsa Perssona945aee2018-04-24 16:53:25 +0200172TEST_P(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200173 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200181 EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
182 EXPECT_EQ(0, handler_->adapt_down_events_);
183 EXPECT_EQ(0, handler_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000184}
185
Åsa Perssona945aee2018-04-24 16:53:25 +0200186TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsIfFieldTrialEnabled) {
187 const bool kDownScaleExpected = !GetParam().empty();
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200188 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200197 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_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200200}
201
202TEST_P(QualityScalerTest, KeepsScaleOnNormalQp) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200203 task_queue_.SendTask([this] { TriggerScale(kKeepScaleAboveLowQp); },
204 RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200205 EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
206 EXPECT_EQ(0, handler_->adapt_down_events_);
207 EXPECT_EQ(0, handler_->adapt_up_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200208}
209
Åsa Perssona945aee2018-04-24 16:53:25 +0200210TEST_P(QualityScalerTest, UpscalesAfterLowQp) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200211 task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200212 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
213 EXPECT_EQ(0, handler_->adapt_down_events_);
214 EXPECT_EQ(1, handler_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000215}
216
Åsa Perssona945aee2018-04-24 16:53:25 +0200217TEST_P(QualityScalerTest, ScalesDownAndBackUp) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200218 task_queue_.SendTask([this] { TriggerScale(kScaleDown); }, RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200219 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
220 EXPECT_EQ(1, handler_->adapt_down_events_);
221 EXPECT_EQ(0, handler_->adapt_up_events_);
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200222 task_queue_.SendTask([this] { TriggerScale(kScaleUp); }, RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200223 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
224 EXPECT_EQ(1, handler_->adapt_down_events_);
225 EXPECT_EQ(1, handler_->adapt_up_events_);
Peter Boström6a688f52015-06-22 08:02:58 +0200226}
kthelgason55a01352017-04-04 02:31:42 -0700227
Åsa Perssona945aee2018-04-24 16:53:25 +0200228TEST_P(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200229 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200237 EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200238 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200245 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
246 EXPECT_EQ(0, handler_->adapt_down_events_);
247 EXPECT_EQ(1, handler_->adapt_up_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200248
249 // Samples should be cleared after an adapt request.
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200250 task_queue_.SendTask(
251 [this] {
252 // Not enough frames to make a decision.
253 qs_->ReportQp(kLowQp, 0);
254 },
255 RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200256 EXPECT_FALSE(handler_->event.Wait(kDefaultTimeoutMs));
257 EXPECT_EQ(0, handler_->adapt_down_events_);
258 EXPECT_EQ(1, handler_->adapt_up_events_);
kthelgason55a01352017-04-04 02:31:42 -0700259}
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200260
Åsa Perssona945aee2018-04-24 16:53:25 +0200261TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200262 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);
Henrik Boström012aa372020-04-27 17:40:55 +0200269 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
270 EXPECT_EQ(1, handler_->adapt_down_events_);
271 EXPECT_EQ(0, handler_->adapt_up_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200272 // Samples cleared.
Danil Chapovaloveb90e6f2019-10-15 10:04:57 +0200273 task_queue_.SendTask(
274 [this] {
275 for (int i = 0; i < kMinFramesNeededToScale; ++i) {
276 qs_->ReportQp(kLowQp, 0);
277 }
278 },
279 RTC_FROM_HERE);
Henrik Boström012aa372020-04-27 17:40:55 +0200280 EXPECT_TRUE(handler_->event.Wait(kDefaultTimeoutMs));
281 EXPECT_EQ(1, handler_->adapt_down_events_);
282 EXPECT_EQ(1, handler_->adapt_up_events_);
283}
284
285TEST_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);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200313}
314
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000315} // namespace webrtc