blob: b17062df5c158ca5afdbba22a2a79dcee366a17a [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/event.h"
17#include "rtc_base/task_queue.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020018#include "test/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "test/gmock.h"
20#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
Yves Gerey665174f2018-06-19 15:03:05 +020031#define DO_SYNC(q, block) \
32 do { \
33 rtc::Event event(false, false); \
34 q->PostTask([this, &event] { \
35 block; \
36 event.Set(); \
37 }); \
38 RTC_CHECK(event.Wait(1000)); \
kthelgasonb12a3e32017-03-30 01:04:55 -070039 } while (0)
40
sprangb1ca0732017-02-01 08:38:12 -080041class MockAdaptationObserver : public AdaptationObserverInterface {
kthelgason876222f2016-11-29 01:44:11 -080042 public:
sprangb1ca0732017-02-01 08:38:12 -080043 MockAdaptationObserver() : event(false, false) {}
44 virtual ~MockAdaptationObserver() {}
kthelgason876222f2016-11-29 01:44:11 -080045
sprangb1ca0732017-02-01 08:38:12 -080046 void AdaptUp(AdaptReason r) override {
47 adapt_up_events_++;
kthelgason876222f2016-11-29 01:44:11 -080048 event.Set();
49 }
sprangb1ca0732017-02-01 08:38:12 -080050 void AdaptDown(AdaptReason r) override {
51 adapt_down_events_++;
kthelgason876222f2016-11-29 01:44:11 -080052 event.Set();
53 }
54
55 rtc::Event event;
sprangb1ca0732017-02-01 08:38:12 -080056 int adapt_up_events_ = 0;
57 int adapt_down_events_ = 0;
kthelgason876222f2016-11-29 01:44:11 -080058};
59
60// Pass a lower sampling period to speed up the tests.
61class QualityScalerUnderTest : public QualityScaler {
62 public:
sprangb1ca0732017-02-01 08:38:12 -080063 explicit QualityScalerUnderTest(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080064 VideoEncoder::QpThresholds thresholds)
65 : QualityScaler(observer, thresholds, 5) {}
66};
67
Åsa Perssona945aee2018-04-24 16:53:25 +020068class QualityScalerTest : public ::testing::Test,
69 public ::testing::WithParamInterface<std::string> {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000070 protected:
Peter Boström17417702015-09-25 17:03:26 +020071 enum ScaleDirection {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020072 kKeepScaleAboveLowQp,
Peter Boström17417702015-09-25 17:03:26 +020073 kKeepScaleAtHighQp,
74 kScaleDown,
75 kScaleDownAboveHighQp,
76 kScaleUp
77 };
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000078
kthelgason876222f2016-11-29 01:44:11 -080079 QualityScalerTest()
Åsa Perssona945aee2018-04-24 16:53:25 +020080 : scoped_field_trial_(GetParam()),
81 q_(new rtc::TaskQueue("QualityScalerTestQueue")),
sprangb1ca0732017-02-01 08:38:12 -080082 observer_(new MockAdaptationObserver()) {
kthelgasonb12a3e32017-03-30 01:04:55 -070083 DO_SYNC(q_, {
kthelgason876222f2016-11-29 01:44:11 -080084 qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020085 observer_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
86 });
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000087 }
88
kthelgason876222f2016-11-29 01:44:11 -080089 ~QualityScalerTest() {
Yves Gerey665174f2018-06-19 15:03:05 +020090 DO_SYNC(q_, { qs_.reset(nullptr); });
kthelgason876222f2016-11-29 01:44:11 -080091 }
92
93 void TriggerScale(ScaleDirection scale_direction) {
94 for (int i = 0; i < kFramerate * 5; ++i) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000095 switch (scale_direction) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020096 case kKeepScaleAboveLowQp:
Åsa Persson04d5f1d2018-04-20 15:19:11 +020097 qs_->ReportQp(kLowQp + 1);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020098 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000099 case kScaleUp:
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200100 qs_->ReportQp(kLowQp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000101 break;
102 case kScaleDown:
Åsa Perssona945aee2018-04-24 16:53:25 +0200103 qs_->ReportDroppedFrameByMediaOpt();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000104 break;
Peter Boström17417702015-09-25 17:03:26 +0200105 case kKeepScaleAtHighQp:
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200106 qs_->ReportQp(kHighQp);
Peter Boström17417702015-09-25 17:03:26 +0200107 break;
108 case kScaleDownAboveHighQp:
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200109 qs_->ReportQp(kHighQp + 1);
Peter Boström17417702015-09-25 17:03:26 +0200110 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000111 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000112 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000113 }
114
Åsa Perssona945aee2018-04-24 16:53:25 +0200115 test::ScopedFieldTrials scoped_field_trial_;
kthelgason876222f2016-11-29 01:44:11 -0800116 std::unique_ptr<rtc::TaskQueue> q_;
117 std::unique_ptr<QualityScaler> qs_;
sprangb1ca0732017-02-01 08:38:12 -0800118 std::unique_ptr<MockAdaptationObserver> observer_;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000119};
120
Åsa Perssona945aee2018-04-24 16:53:25 +0200121INSTANTIATE_TEST_CASE_P(
122 FieldTrials,
123 QualityScalerTest,
124 ::testing::Values(
125 "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.9,0.99,1/",
126 ""));
127
128TEST_P(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700129 DO_SYNC(q_, { TriggerScale(kScaleDown); });
kthelgason86cf9a22016-12-01 02:57:01 -0800130 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800131 EXPECT_EQ(1, observer_->adapt_down_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200132 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000133}
134
Åsa Perssona945aee2018-04-24 16:53:25 +0200135TEST_P(QualityScalerTest, KeepsScaleAtHighQp) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700136 DO_SYNC(q_, { TriggerScale(kKeepScaleAtHighQp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800137 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800138 EXPECT_EQ(0, observer_->adapt_down_events_);
139 EXPECT_EQ(0, observer_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200140}
141
Åsa Perssona945aee2018-04-24 16:53:25 +0200142TEST_P(QualityScalerTest, DownscalesAboveHighQp) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700143 DO_SYNC(q_, { TriggerScale(kScaleDownAboveHighQp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800144 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800145 EXPECT_EQ(1, observer_->adapt_down_events_);
146 EXPECT_EQ(0, observer_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200147}
148
Åsa Perssona945aee2018-04-24 16:53:25 +0200149TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700150 DO_SYNC(q_, {
kthelgason55a01352017-04-04 02:31:42 -0700151 for (int i = 0; i < kFramerate * 5; ++i) {
Åsa Perssona945aee2018-04-24 16:53:25 +0200152 qs_->ReportDroppedFrameByMediaOpt();
153 qs_->ReportDroppedFrameByMediaOpt();
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200154 qs_->ReportQp(kHighQp);
kthelgason55a01352017-04-04 02:31:42 -0700155 }
kthelgason876222f2016-11-29 01:44:11 -0800156 });
kthelgason86cf9a22016-12-01 02:57:01 -0800157 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800158 EXPECT_EQ(1, observer_->adapt_down_events_);
159 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000160}
161
Åsa Perssona945aee2018-04-24 16:53:25 +0200162TEST_P(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700163 DO_SYNC(q_, {
kthelgason55a01352017-04-04 02:31:42 -0700164 for (int i = 0; i < kFramerate * 5; ++i) {
Åsa Perssona945aee2018-04-24 16:53:25 +0200165 qs_->ReportDroppedFrameByMediaOpt();
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200166 qs_->ReportQp(kHighQp);
kthelgason55a01352017-04-04 02:31:42 -0700167 }
kthelgason876222f2016-11-29 01:44:11 -0800168 });
kthelgason86cf9a22016-12-01 02:57:01 -0800169 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800170 EXPECT_EQ(0, observer_->adapt_down_events_);
171 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000172}
173
Åsa Perssona945aee2018-04-24 16:53:25 +0200174TEST_P(QualityScalerTest, DownscalesAfterTwoThirdsIfFieldTrialEnabled) {
175 const bool kDownScaleExpected = !GetParam().empty();
176 DO_SYNC(q_, {
177 for (int i = 0; i < kFramerate * 5; ++i) {
178 qs_->ReportDroppedFrameByMediaOpt();
179 qs_->ReportDroppedFrameByEncoder();
180 qs_->ReportQp(kHighQp);
181 }
182 });
183 EXPECT_EQ(kDownScaleExpected, observer_->event.Wait(kDefaultTimeoutMs));
184 EXPECT_EQ(kDownScaleExpected ? 1 : 0, observer_->adapt_down_events_);
185 EXPECT_EQ(0, observer_->adapt_up_events_);
186}
187
188TEST_P(QualityScalerTest, KeepsScaleOnNormalQp) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200189 DO_SYNC(q_, { TriggerScale(kKeepScaleAboveLowQp); });
190 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
191 EXPECT_EQ(0, observer_->adapt_down_events_);
192 EXPECT_EQ(0, observer_->adapt_up_events_);
193}
194
Åsa Perssona945aee2018-04-24 16:53:25 +0200195TEST_P(QualityScalerTest, UpscalesAfterLowQp) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700196 DO_SYNC(q_, { TriggerScale(kScaleUp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800197 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800198 EXPECT_EQ(0, observer_->adapt_down_events_);
199 EXPECT_EQ(1, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000200}
201
Åsa Perssona945aee2018-04-24 16:53:25 +0200202TEST_P(QualityScalerTest, ScalesDownAndBackUp) {
kthelgasonb12a3e32017-03-30 01:04:55 -0700203 DO_SYNC(q_, { TriggerScale(kScaleDown); });
kthelgason86cf9a22016-12-01 02:57:01 -0800204 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800205 EXPECT_EQ(1, observer_->adapt_down_events_);
206 EXPECT_EQ(0, observer_->adapt_up_events_);
kthelgasonb12a3e32017-03-30 01:04:55 -0700207 DO_SYNC(q_, { TriggerScale(kScaleUp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800208 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800209 EXPECT_EQ(1, observer_->adapt_down_events_);
210 EXPECT_EQ(1, observer_->adapt_up_events_);
Peter Boström6a688f52015-06-22 08:02:58 +0200211}
kthelgason55a01352017-04-04 02:31:42 -0700212
Åsa Perssona945aee2018-04-24 16:53:25 +0200213TEST_P(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
kthelgason55a01352017-04-04 02:31:42 -0700214 DO_SYNC(q_, {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200215 // Not enough frames to make a decision.
216 for (int i = 0; i < kMinFramesNeededToScale - 1; ++i) {
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200217 qs_->ReportQp(kLowQp);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200218 }
219 });
kthelgason55a01352017-04-04 02:31:42 -0700220 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
221 DO_SYNC(q_, {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200222 // Send 1 more. Enough frames observed, should result in an adapt request.
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200223 qs_->ReportQp(kLowQp);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200224 });
kthelgason55a01352017-04-04 02:31:42 -0700225 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
226 EXPECT_EQ(0, observer_->adapt_down_events_);
227 EXPECT_EQ(1, observer_->adapt_up_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200228
229 // Samples should be cleared after an adapt request.
230 DO_SYNC(q_, {
231 // Not enough frames to make a decision.
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200232 qs_->ReportQp(kLowQp);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200233 });
234 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
235 EXPECT_EQ(0, observer_->adapt_down_events_);
236 EXPECT_EQ(1, observer_->adapt_up_events_);
kthelgason55a01352017-04-04 02:31:42 -0700237}
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200238
Åsa Perssona945aee2018-04-24 16:53:25 +0200239TEST_P(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200240 DO_SYNC(q_, {
241 for (int i = 0; i < kMinFramesNeededToScale; ++i) {
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200242 qs_->ReportQp(kHighQp + 1);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200243 }
244 });
245 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
246 EXPECT_EQ(1, observer_->adapt_down_events_);
247 EXPECT_EQ(0, observer_->adapt_up_events_);
248 // Samples cleared.
249 DO_SYNC(q_, {
250 for (int i = 0; i < kMinFramesNeededToScale; ++i) {
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200251 qs_->ReportQp(kLowQp);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200252 }
253 });
254 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
255 EXPECT_EQ(1, observer_->adapt_down_events_);
256 EXPECT_EQ(1, observer_->adapt_up_events_);
257}
258
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000259} // namespace webrtc
kthelgasonb12a3e32017-03-30 01:04:55 -0700260#undef DO_SYNC