blob: ea99a7d287e64eab90d12fc6f69f04aeabe6dd95 [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>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "rtc_base/event.h"
16#include "rtc_base/task_queue.h"
17#include "test/gmock.h"
18#include "test/gtest.h"
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000019
20namespace webrtc {
21namespace {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000022static const int kFramerate = 30;
23static const int kLowQp = 15;
Peter Boström17417702015-09-25 17:03:26 +020024static const int kHighQp = 40;
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020025static const int kMinFramesNeededToScale = 60; // From quality_scaler.cc.
kthelgason86cf9a22016-12-01 02:57:01 -080026static const size_t kDefaultTimeoutMs = 150;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000027} // namespace
28
kthelgasonb12a3e32017-03-30 01:04:55 -070029#define DO_SYNC(q, block) do { \
30 rtc::Event event(false, false); \
31 q->PostTask([this, &event] { \
32 block; \
33 event.Set(); \
34 }); \
35 RTC_CHECK(event.Wait(1000)); \
36 } while (0)
37
38
sprangb1ca0732017-02-01 08:38:12 -080039class MockAdaptationObserver : public AdaptationObserverInterface {
kthelgason876222f2016-11-29 01:44:11 -080040 public:
sprangb1ca0732017-02-01 08:38:12 -080041 MockAdaptationObserver() : event(false, false) {}
42 virtual ~MockAdaptationObserver() {}
kthelgason876222f2016-11-29 01:44:11 -080043
sprangb1ca0732017-02-01 08:38:12 -080044 void AdaptUp(AdaptReason r) override {
45 adapt_up_events_++;
kthelgason876222f2016-11-29 01:44:11 -080046 event.Set();
47 }
sprangb1ca0732017-02-01 08:38:12 -080048 void AdaptDown(AdaptReason r) override {
49 adapt_down_events_++;
kthelgason876222f2016-11-29 01:44:11 -080050 event.Set();
51 }
52
53 rtc::Event event;
sprangb1ca0732017-02-01 08:38:12 -080054 int adapt_up_events_ = 0;
55 int adapt_down_events_ = 0;
kthelgason876222f2016-11-29 01:44:11 -080056};
57
58// Pass a lower sampling period to speed up the tests.
59class QualityScalerUnderTest : public QualityScaler {
60 public:
sprangb1ca0732017-02-01 08:38:12 -080061 explicit QualityScalerUnderTest(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080062 VideoEncoder::QpThresholds thresholds)
63 : QualityScaler(observer, thresholds, 5) {}
64};
65
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000066class QualityScalerTest : public ::testing::Test {
67 protected:
Peter Boström17417702015-09-25 17:03:26 +020068 enum ScaleDirection {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020069 kKeepScaleAboveLowQp,
Peter Boström17417702015-09-25 17:03:26 +020070 kKeepScaleAtHighQp,
71 kScaleDown,
72 kScaleDownAboveHighQp,
73 kScaleUp
74 };
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000075
kthelgason876222f2016-11-29 01:44:11 -080076 QualityScalerTest()
77 : q_(new rtc::TaskQueue("QualityScalerTestQueue")),
sprangb1ca0732017-02-01 08:38:12 -080078 observer_(new MockAdaptationObserver()) {
kthelgasonb12a3e32017-03-30 01:04:55 -070079 DO_SYNC(q_, {
kthelgason876222f2016-11-29 01:44:11 -080080 qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020081 observer_.get(), VideoEncoder::QpThresholds(kLowQp, kHighQp)));
82 });
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000083 }
84
kthelgason876222f2016-11-29 01:44:11 -080085 ~QualityScalerTest() {
kthelgasonb12a3e32017-03-30 01:04:55 -070086 DO_SYNC(q_, {qs_.reset(nullptr);});
kthelgason876222f2016-11-29 01:44:11 -080087 }
88
89 void TriggerScale(ScaleDirection scale_direction) {
90 for (int i = 0; i < kFramerate * 5; ++i) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000091 switch (scale_direction) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020092 case kKeepScaleAboveLowQp:
93 qs_->ReportQP(kLowQp + 1);
94 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000095 case kScaleUp:
kthelgason876222f2016-11-29 01:44:11 -080096 qs_->ReportQP(kLowQp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000097 break;
98 case kScaleDown:
kthelgason876222f2016-11-29 01:44:11 -080099 qs_->ReportDroppedFrame();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000100 break;
Peter Boström17417702015-09-25 17:03:26 +0200101 case kKeepScaleAtHighQp:
kthelgason876222f2016-11-29 01:44:11 -0800102 qs_->ReportQP(kHighQp);
Peter Boström17417702015-09-25 17:03:26 +0200103 break;
104 case kScaleDownAboveHighQp:
kthelgason876222f2016-11-29 01:44:11 -0800105 qs_->ReportQP(kHighQp + 1);
Peter Boström17417702015-09-25 17:03:26 +0200106 break;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000107 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000108 }
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000109 }
110
kthelgason876222f2016-11-29 01:44:11 -0800111 std::unique_ptr<rtc::TaskQueue> q_;
112 std::unique_ptr<QualityScaler> qs_;
sprangb1ca0732017-02-01 08:38:12 -0800113 std::unique_ptr<MockAdaptationObserver> observer_;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000114};
115
kthelgasonb12a3e32017-03-30 01:04:55 -0700116TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
117 DO_SYNC(q_, { TriggerScale(kScaleDown); });
kthelgason86cf9a22016-12-01 02:57:01 -0800118 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800119 EXPECT_EQ(1, observer_->adapt_down_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200120 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000121}
122
kthelgasonb12a3e32017-03-30 01:04:55 -0700123TEST_F(QualityScalerTest, KeepsScaleAtHighQp) {
124 DO_SYNC(q_, { TriggerScale(kKeepScaleAtHighQp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800125 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800126 EXPECT_EQ(0, observer_->adapt_down_events_);
127 EXPECT_EQ(0, observer_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200128}
129
kthelgasonb12a3e32017-03-30 01:04:55 -0700130TEST_F(QualityScalerTest, DownscalesAboveHighQp) {
131 DO_SYNC(q_, { TriggerScale(kScaleDownAboveHighQp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800132 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800133 EXPECT_EQ(1, observer_->adapt_down_events_);
134 EXPECT_EQ(0, observer_->adapt_up_events_);
Peter Boström17417702015-09-25 17:03:26 +0200135}
136
kthelgasonb12a3e32017-03-30 01:04:55 -0700137TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
138 DO_SYNC(q_, {
kthelgason55a01352017-04-04 02:31:42 -0700139 for (int i = 0; i < kFramerate * 5; ++i) {
140 qs_->ReportDroppedFrame();
141 qs_->ReportDroppedFrame();
142 qs_->ReportQP(kHighQp);
143 }
kthelgason876222f2016-11-29 01:44:11 -0800144 });
kthelgason86cf9a22016-12-01 02:57:01 -0800145 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800146 EXPECT_EQ(1, observer_->adapt_down_events_);
147 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000148}
149
kthelgasonb12a3e32017-03-30 01:04:55 -0700150TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
151 DO_SYNC(q_, {
kthelgason55a01352017-04-04 02:31:42 -0700152 for (int i = 0; i < kFramerate * 5; ++i) {
153 qs_->ReportDroppedFrame();
154 qs_->ReportQP(kHighQp);
155 }
kthelgason876222f2016-11-29 01:44:11 -0800156 });
kthelgason86cf9a22016-12-01 02:57:01 -0800157 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800158 EXPECT_EQ(0, observer_->adapt_down_events_);
159 EXPECT_EQ(0, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000160}
161
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200162TEST_F(QualityScalerTest, KeepsScaleOnNormalQp) {
163 DO_SYNC(q_, { TriggerScale(kKeepScaleAboveLowQp); });
164 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
165 EXPECT_EQ(0, observer_->adapt_down_events_);
166 EXPECT_EQ(0, observer_->adapt_up_events_);
167}
168
kthelgasonb12a3e32017-03-30 01:04:55 -0700169TEST_F(QualityScalerTest, UpscalesAfterLowQp) {
170 DO_SYNC(q_, { TriggerScale(kScaleUp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800171 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800172 EXPECT_EQ(0, observer_->adapt_down_events_);
173 EXPECT_EQ(1, observer_->adapt_up_events_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000174}
175
kthelgasonb12a3e32017-03-30 01:04:55 -0700176TEST_F(QualityScalerTest, ScalesDownAndBackUp) {
177 DO_SYNC(q_, { TriggerScale(kScaleDown); });
kthelgason86cf9a22016-12-01 02:57:01 -0800178 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800179 EXPECT_EQ(1, observer_->adapt_down_events_);
180 EXPECT_EQ(0, observer_->adapt_up_events_);
kthelgasonb12a3e32017-03-30 01:04:55 -0700181 DO_SYNC(q_, { TriggerScale(kScaleUp); });
kthelgason86cf9a22016-12-01 02:57:01 -0800182 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
sprangb1ca0732017-02-01 08:38:12 -0800183 EXPECT_EQ(1, observer_->adapt_down_events_);
184 EXPECT_EQ(1, observer_->adapt_up_events_);
Peter Boström6a688f52015-06-22 08:02:58 +0200185}
kthelgason55a01352017-04-04 02:31:42 -0700186
187TEST_F(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
188 DO_SYNC(q_, {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200189 // Not enough frames to make a decision.
190 for (int i = 0; i < kMinFramesNeededToScale - 1; ++i) {
191 qs_->ReportQP(kLowQp);
192 }
193 });
kthelgason55a01352017-04-04 02:31:42 -0700194 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
195 DO_SYNC(q_, {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200196 // Send 1 more. Enough frames observed, should result in an adapt request.
197 qs_->ReportQP(kLowQp);
198 });
kthelgason55a01352017-04-04 02:31:42 -0700199 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
200 EXPECT_EQ(0, observer_->adapt_down_events_);
201 EXPECT_EQ(1, observer_->adapt_up_events_);
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200202
203 // Samples should be cleared after an adapt request.
204 DO_SYNC(q_, {
205 // Not enough frames to make a decision.
206 qs_->ReportQP(kLowQp);
207 });
208 EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
209 EXPECT_EQ(0, observer_->adapt_down_events_);
210 EXPECT_EQ(1, observer_->adapt_up_events_);
kthelgason55a01352017-04-04 02:31:42 -0700211}
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200212
213TEST_F(QualityScalerTest, ScalesDownAndBackUpWithMinFramesNeeded) {
214 DO_SYNC(q_, {
215 for (int i = 0; i < kMinFramesNeededToScale; ++i) {
216 qs_->ReportQP(kHighQp + 1);
217 }
218 });
219 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
220 EXPECT_EQ(1, observer_->adapt_down_events_);
221 EXPECT_EQ(0, observer_->adapt_up_events_);
222 // Samples cleared.
223 DO_SYNC(q_, {
224 for (int i = 0; i < kMinFramesNeededToScale; ++i) {
225 qs_->ReportQP(kLowQp);
226 }
227 });
228 EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
229 EXPECT_EQ(1, observer_->adapt_down_events_);
230 EXPECT_EQ(1, observer_->adapt_up_events_);
231}
232
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000233} // namespace webrtc
kthelgasonb12a3e32017-03-30 01:04:55 -0700234#undef DO_SYNC