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 
15 #include "rtc_base/event.h"
16 #include "rtc_base/task_queue.h"
17 #include "test/gmock.h"
18 #include "test/gtest.h"
19 
20 namespace webrtc {
21 namespace {
22 static const int kFramerate = 30;
23 static const int kLowQp = 15;
24 static const int kLowQpThreshold = 18;
25 static const int kHighQp = 40;
26 static const size_t kDefaultTimeoutMs = 150;
27 }  // namespace
28 
29 #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 
39 class MockAdaptationObserver : public AdaptationObserverInterface {
40  public:
MockAdaptationObserver()41   MockAdaptationObserver() : event(false, false) {}
~MockAdaptationObserver()42   virtual ~MockAdaptationObserver() {}
43 
AdaptUp(AdaptReason r)44   void AdaptUp(AdaptReason r) override {
45     adapt_up_events_++;
46     event.Set();
47   }
AdaptDown(AdaptReason r)48   void AdaptDown(AdaptReason r) override {
49     adapt_down_events_++;
50     event.Set();
51   }
52 
53   rtc::Event event;
54   int adapt_up_events_ = 0;
55   int adapt_down_events_ = 0;
56 };
57 
58 // Pass a lower sampling period to speed up the tests.
59 class QualityScalerUnderTest : public QualityScaler {
60  public:
QualityScalerUnderTest(AdaptationObserverInterface * observer,VideoEncoder::QpThresholds thresholds)61   explicit QualityScalerUnderTest(AdaptationObserverInterface* observer,
62                                   VideoEncoder::QpThresholds thresholds)
63       : QualityScaler(observer, thresholds, 5) {}
64 };
65 
66 class QualityScalerTest : public ::testing::Test {
67  protected:
68   enum ScaleDirection {
69     kKeepScaleAtHighQp,
70     kScaleDown,
71     kScaleDownAboveHighQp,
72     kScaleUp
73   };
74 
QualityScalerTest()75   QualityScalerTest()
76       : q_(new rtc::TaskQueue("QualityScalerTestQueue")),
77         observer_(new MockAdaptationObserver()) {
78     DO_SYNC(q_, {
79       qs_ = std::unique_ptr<QualityScaler>(new QualityScalerUnderTest(
80           observer_.get(),
81           VideoEncoder::QpThresholds(kLowQpThreshold, kHighQp)));});
82   }
83 
~QualityScalerTest()84   ~QualityScalerTest() {
85     DO_SYNC(q_, {qs_.reset(nullptr);});
86   }
87 
TriggerScale(ScaleDirection scale_direction)88   void TriggerScale(ScaleDirection scale_direction) {
89     for (int i = 0; i < kFramerate * 5; ++i) {
90       switch (scale_direction) {
91         case kScaleUp:
92           qs_->ReportQP(kLowQp);
93           break;
94         case kScaleDown:
95           qs_->ReportDroppedFrame();
96           break;
97         case kKeepScaleAtHighQp:
98           qs_->ReportQP(kHighQp);
99           break;
100         case kScaleDownAboveHighQp:
101           qs_->ReportQP(kHighQp + 1);
102           break;
103       }
104     }
105   }
106 
107   std::unique_ptr<rtc::TaskQueue> q_;
108   std::unique_ptr<QualityScaler> qs_;
109   std::unique_ptr<MockAdaptationObserver> observer_;
110 };
111 
TEST_F(QualityScalerTest,DownscalesAfterContinuousFramedrop)112 TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) {
113   DO_SYNC(q_, { TriggerScale(kScaleDown); });
114   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
115   EXPECT_EQ(1, observer_->adapt_down_events_);
116 }
117 
TEST_F(QualityScalerTest,KeepsScaleAtHighQp)118 TEST_F(QualityScalerTest, KeepsScaleAtHighQp) {
119   DO_SYNC(q_, { TriggerScale(kKeepScaleAtHighQp); });
120   EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
121   EXPECT_EQ(0, observer_->adapt_down_events_);
122   EXPECT_EQ(0, observer_->adapt_up_events_);
123 }
124 
TEST_F(QualityScalerTest,DownscalesAboveHighQp)125 TEST_F(QualityScalerTest, DownscalesAboveHighQp) {
126   DO_SYNC(q_, { TriggerScale(kScaleDownAboveHighQp); });
127   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
128   EXPECT_EQ(1, observer_->adapt_down_events_);
129   EXPECT_EQ(0, observer_->adapt_up_events_);
130 }
131 
TEST_F(QualityScalerTest,DownscalesAfterTwoThirdsFramedrop)132 TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) {
133   DO_SYNC(q_, {
134     for (int i = 0; i < kFramerate * 5; ++i) {
135       qs_->ReportDroppedFrame();
136       qs_->ReportDroppedFrame();
137       qs_->ReportQP(kHighQp);
138     }
139   });
140   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
141   EXPECT_EQ(1, observer_->adapt_down_events_);
142   EXPECT_EQ(0, observer_->adapt_up_events_);
143 }
144 
TEST_F(QualityScalerTest,DoesNotDownscaleOnNormalQp)145 TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) {
146   DO_SYNC(q_, { TriggerScale(kScaleDownAboveHighQp); });
147   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
148   EXPECT_EQ(1, observer_->adapt_down_events_);
149   EXPECT_EQ(0, observer_->adapt_up_events_);
150 }
151 
TEST_F(QualityScalerTest,DoesNotDownscaleAfterHalfFramedrop)152 TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) {
153   DO_SYNC(q_, {
154     for (int i = 0; i < kFramerate * 5; ++i) {
155       qs_->ReportDroppedFrame();
156       qs_->ReportQP(kHighQp);
157     }
158   });
159   EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
160   EXPECT_EQ(0, observer_->adapt_down_events_);
161   EXPECT_EQ(0, observer_->adapt_up_events_);
162 }
163 
TEST_F(QualityScalerTest,UpscalesAfterLowQp)164 TEST_F(QualityScalerTest, UpscalesAfterLowQp) {
165   DO_SYNC(q_, { TriggerScale(kScaleUp); });
166   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
167   EXPECT_EQ(0, observer_->adapt_down_events_);
168   EXPECT_EQ(1, observer_->adapt_up_events_);
169 }
170 
TEST_F(QualityScalerTest,ScalesDownAndBackUp)171 TEST_F(QualityScalerTest, ScalesDownAndBackUp) {
172   DO_SYNC(q_, { TriggerScale(kScaleDown); });
173   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
174   EXPECT_EQ(1, observer_->adapt_down_events_);
175   EXPECT_EQ(0, observer_->adapt_up_events_);
176   DO_SYNC(q_, { TriggerScale(kScaleUp); });
177   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
178   EXPECT_EQ(1, observer_->adapt_down_events_);
179   EXPECT_EQ(1, observer_->adapt_up_events_);
180 }
181 
TEST_F(QualityScalerTest,DoesNotScaleUntilEnoughFramesObserved)182 TEST_F(QualityScalerTest, DoesNotScaleUntilEnoughFramesObserved) {
183   DO_SYNC(q_, {
184       // Send 30 frames. This should not be enough to make a decision.
185       for (int i = 0; i < kFramerate; ++i) {
186         qs_->ReportQP(kLowQp);
187       }
188     });
189   EXPECT_FALSE(observer_->event.Wait(kDefaultTimeoutMs));
190   DO_SYNC(q_, {
191       // Send 30 more. This should result in an adapt request as
192       // enough frames have now been observed.
193       for (int i = 0; i < kFramerate; ++i) {
194         qs_->ReportQP(kLowQp);
195       }
196     });
197   EXPECT_TRUE(observer_->event.Wait(kDefaultTimeoutMs));
198   EXPECT_EQ(0, observer_->adapt_down_events_);
199   EXPECT_EQ(1, observer_->adapt_up_events_);
200 }
201 }  // namespace webrtc
202 #undef DO_SYNC
203