OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/modules/video_coding/utility/quality_scaler.h" | 11 #include "webrtc/modules/video_coding/utility/quality_scaler.h" |
12 | 12 |
13 #include <memory> | |
14 | |
15 #include "webrtc/base/event.h" | |
16 #include "webrtc/base/task_queue.h" | |
17 #include "webrtc/common_video/rotation.h" | |
magjed_webrtc
2016/10/27 11:45:58
This include looks unused.
kthelgason
2016/10/28 13:20:15
Acknowledged.
| |
18 #include "webrtc/video_frame.h" | |
magjed_webrtc
2016/10/27 11:45:58
This include looks unused as well.
kthelgason
2016/10/28 13:20:16
Acknowledged.
| |
19 #include "webrtc/test/gmock.h" | |
13 #include "webrtc/test/gtest.h" | 20 #include "webrtc/test/gtest.h" |
14 | 21 |
15 namespace webrtc { | 22 namespace webrtc { |
16 namespace { | 23 namespace { |
17 static const int kNumSeconds = 10; | |
18 static const int kWidth = 1920; | |
19 static const int kHeight = 1080; | |
20 static const int kFramerate = 30; | 24 static const int kFramerate = 30; |
21 static const int kLowQp = 15; | 25 static const int kLowQp = 15; |
22 static const int kNormalQp = 30; | |
23 static const int kLowQpThreshold = 18; | 26 static const int kLowQpThreshold = 18; |
24 static const int kHighQp = 40; | 27 static const int kHighQp = 40; |
25 static const int kDisabledBadQpThreshold = 64; | |
26 static const int kLowInitialBitrateKbps = 300; | |
27 // These values need to be in sync with corresponding constants | |
28 // in quality_scaler.cc | |
29 static const int kMeasureSecondsFastUpscale = 2; | |
30 static const int kMeasureSecondsUpscale = 5; | |
31 static const int kMeasureSecondsDownscale = 5; | |
32 static const int kMinDownscaleDimension = 140; | |
33 } // namespace | 28 } // namespace |
34 | 29 |
30 class MockScaleObserver : public ScalingObserverInterface { | |
31 public: | |
32 MockScaleObserver() {} | |
33 virtual ~MockScaleObserver() {} | |
34 | |
35 MOCK_METHOD1(ScaleUp, void(ScaleReason)); | |
36 MOCK_METHOD1(ScaleDown, void(ScaleReason)); | |
37 }; | |
38 | |
39 // Override GetTimeoutMs to speed up the tests. | |
40 class QualityScalerUnderTest : public QualityScaler { | |
41 public: | |
42 explicit QualityScalerUnderTest(ScalingObserverInterface* observer) | |
43 : QualityScaler(observer) {} | |
44 int64_t GetTimeoutMs() { return 5; } | |
45 }; | |
46 | |
35 class QualityScalerTest : public ::testing::Test { | 47 class QualityScalerTest : public ::testing::Test { |
36 protected: | 48 protected: |
37 enum ScaleDirection { | 49 enum ScaleDirection { |
38 kKeepScaleAtHighQp, | 50 kKeepScaleAtHighQp, |
39 kScaleDown, | 51 kScaleDown, |
40 kScaleDownAboveHighQp, | 52 kScaleDownAboveHighQp, |
41 kScaleUp | 53 kScaleUp |
42 }; | 54 }; |
43 | 55 |
44 QualityScalerTest() { | 56 QualityScalerTest() |
45 input_frame_ = I420Buffer::Create(kWidth, kHeight); | 57 : q(new rtc::TaskQueue("QualityScalerTestQueue")), |
46 qs_.Init(kLowQpThreshold, kHighQp, 0, kWidth, kHeight, kFramerate); | 58 observer_(new MockScaleObserver()) { |
47 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | 59 rtc::Event event(false, false); |
60 qs_ = std::unique_ptr<QualityScaler>( | |
61 new QualityScalerUnderTest(observer_.get())); | |
62 q->PostTask([this, &event] { | |
63 qs_->Init(QualityScaler::QPThresholds(kLowQpThreshold, kHighQp)); | |
64 event.Set(); | |
65 }); | |
66 EXPECT_TRUE(event.Wait(1000)); | |
sprang_webrtc
2016/10/27 13:43:48
Perhaps a kDefaultTimeoutMs constant somewhere?
kthelgason
2016/10/28 13:20:15
Done.
| |
48 } | 67 } |
49 | 68 |
50 bool TriggerScale(ScaleDirection scale_direction) { | 69 ~QualityScalerTest() { |
51 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | 70 rtc::Event event(false, false); |
52 int initial_width = qs_.GetScaledResolution().width; | 71 q->PostTask([this, &event] { |
53 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 72 qs_->Stop(); |
73 event.Set(); | |
74 }); | |
75 EXPECT_TRUE(event.Wait(1000)); | |
76 } | |
77 | |
78 void TriggerScale(ScaleDirection scale_direction) { | |
79 for (int i = 0; i < kFramerate * 5; ++i) { | |
54 switch (scale_direction) { | 80 switch (scale_direction) { |
55 case kScaleUp: | 81 case kScaleUp: |
56 qs_.ReportQP(kLowQp); | 82 qs_->ReportQP(kLowQp); |
57 break; | 83 break; |
58 case kScaleDown: | 84 case kScaleDown: |
59 qs_.ReportDroppedFrame(); | 85 qs_->ReportDroppedFrame(); |
60 break; | 86 break; |
61 case kKeepScaleAtHighQp: | 87 case kKeepScaleAtHighQp: |
62 qs_.ReportQP(kHighQp); | 88 qs_->ReportQP(kHighQp); |
63 break; | 89 break; |
64 case kScaleDownAboveHighQp: | 90 case kScaleDownAboveHighQp: |
65 qs_.ReportQP(kHighQp + 1); | 91 qs_->ReportQP(kHighQp + 1); |
66 break; | 92 break; |
67 } | 93 } |
68 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
69 if (qs_.GetScaledResolution().width != initial_width) | |
70 return true; | |
71 } | 94 } |
72 | |
73 return false; | |
74 } | 95 } |
75 | 96 |
76 void ExpectOriginalFrame() { | 97 std::unique_ptr<rtc::TaskQueue> q; |
77 EXPECT_EQ(input_frame_, qs_.GetScaledBuffer(input_frame_)) | 98 std::unique_ptr<QualityScaler> qs_; |
78 << "Using scaled frame instead of original input."; | 99 std::unique_ptr<MockScaleObserver> observer_; |
79 } | |
80 | 100 |
81 void ExpectScaleUsingReportedResolution() { | 101 static const auto reason_ = ScalingObserverInterface::kQuality; |
82 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
83 QualityScaler::Resolution res = qs_.GetScaledResolution(); | |
84 rtc::scoped_refptr<VideoFrameBuffer> scaled_frame = | |
85 qs_.GetScaledBuffer(input_frame_); | |
86 EXPECT_EQ(res.width, scaled_frame->width()); | |
87 EXPECT_EQ(res.height, scaled_frame->height()); | |
88 } | |
89 | |
90 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | |
91 | |
92 void DoesNotDownscaleFrameDimensions(int width, int height); | |
93 | |
94 void DownscaleEndsAt(int input_width, | |
95 int input_height, | |
96 int end_width, | |
97 int end_height); | |
98 | |
99 QualityScaler qs_; | |
100 rtc::scoped_refptr<VideoFrameBuffer> input_frame_; | |
101 }; | 102 }; |
102 | 103 |
103 TEST_F(QualityScalerTest, UsesOriginalFrameInitially) { | 104 #define WAIT_FOR_MS(x) \ |
104 ExpectOriginalFrame(); | 105 do { \ |
magjed_webrtc
2016/10/27 11:45:58
Why a do-while block and not just a block?
{
rtc
sprang_webrtc
2016/10/27 13:43:48
This is good practice. See for instance: http://st
kthelgason
2016/10/28 13:20:15
Sure, I would not use this in production code, but
| |
105 } | 106 rtc::Event ev(false, false); \ |
106 | 107 ev.Wait(x); \ |
107 TEST_F(QualityScalerTest, ReportsOriginalResolutionInitially) { | 108 } while (0) |
108 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
109 QualityScaler::Resolution res = qs_.GetScaledResolution(); | |
110 EXPECT_EQ(input_frame_->width(), res.width); | |
111 EXPECT_EQ(input_frame_->height(), res.height); | |
112 } | |
113 | 109 |
114 TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) { | 110 TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) { |
115 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " << kNumSeconds | 111 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(1); |
magjed_webrtc
2016/10/27 11:45:58
How do we know that it will be called exactly once
kthelgason
2016/10/28 13:20:16
After a scaledown event stats get flushed. If Scal
| |
116 << " seconds."; | 112 q->PostTask([this] { TriggerScale(kScaleDown); }); |
117 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 113 WAIT_FOR_MS(50); |
magjed_webrtc
2016/10/27 11:45:58
Will this sleep for 50ms unconditionally? Can't we
kthelgason
2016/10/28 13:20:16
Yes, for now this is an unconditional wait. I don'
| |
118 EXPECT_LT(res.width, input_frame_->width()); | |
119 EXPECT_LT(res.height, input_frame_->height()); | |
120 } | 114 } |
121 | 115 |
122 TEST_F(QualityScalerTest, KeepsScaleAtHighQp) { | 116 TEST_F(QualityScalerTest, KeepsScaleAtHighQp) { |
123 EXPECT_FALSE(TriggerScale(kKeepScaleAtHighQp)) | 117 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(0); |
124 << "Downscale at high threshold which should keep scale."; | 118 EXPECT_CALL(*observer_, ScaleUp(reason_)).Times(0); |
125 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 119 q->PostTask([this] { TriggerScale(kKeepScaleAtHighQp); }); |
126 EXPECT_EQ(res.width, input_frame_->width()); | 120 WAIT_FOR_MS(50); |
127 EXPECT_EQ(res.height, input_frame_->height()); | |
128 } | 121 } |
129 | 122 |
130 TEST_F(QualityScalerTest, DownscalesAboveHighQp) { | 123 TEST_F(QualityScalerTest, DownscalesAboveHighQp) { |
131 EXPECT_TRUE(TriggerScale(kScaleDownAboveHighQp)) | 124 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(1); |
132 << "No downscale within " << kNumSeconds << " seconds."; | 125 q->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); }); |
133 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 126 WAIT_FOR_MS(50); |
134 EXPECT_LT(res.width, input_frame_->width()); | |
135 EXPECT_LT(res.height, input_frame_->height()); | |
136 } | 127 } |
137 | 128 |
138 TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { | 129 TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { |
139 for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) { | 130 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(1); |
140 qs_.ReportQP(kNormalQp); | 131 q->PostTask([this] { |
141 qs_.ReportDroppedFrame(); | 132 qs_->ReportDroppedFrame(); |
142 qs_.ReportDroppedFrame(); | 133 qs_->ReportDroppedFrame(); |
143 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | 134 qs_->ReportQP(kHighQp); |
144 if (qs_.GetScaledResolution().width < input_frame_->width()) | 135 }); |
145 return; | 136 WAIT_FOR_MS(50); |
146 } | |
147 | |
148 FAIL() << "No downscale within " << kNumSeconds << " seconds."; | |
149 } | 137 } |
150 | 138 |
151 TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { | 139 TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { |
152 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 140 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(1); |
153 qs_.ReportQP(kNormalQp); | 141 q->PostTask([this] { TriggerScale(kScaleDownAboveHighQp); }); |
154 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | 142 WAIT_FOR_MS(50); |
155 ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width) | |
156 << "Unexpected scale on half framedrop."; | |
157 } | |
158 } | 143 } |
159 | 144 |
160 TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) { | 145 TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) { |
161 for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) { | 146 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(0); |
162 qs_.ReportQP(kNormalQp); | 147 q->PostTask([this] { |
163 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | 148 qs_->ReportDroppedFrame(); |
164 ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width) | 149 qs_->ReportQP(kHighQp); |
165 << "Unexpected scale on half framedrop."; | 150 }); |
166 | 151 WAIT_FOR_MS(50); |
167 qs_.ReportDroppedFrame(); | |
168 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
169 ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width) | |
170 << "Unexpected scale on half framedrop."; | |
171 } | |
172 } | 152 } |
173 | 153 |
174 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { | 154 TEST_F(QualityScalerTest, UpscalesAfterLowQp) { |
175 const int initial_min_dimension = | 155 EXPECT_CALL(*observer_, ScaleUp(reason_)).Times(1); |
176 input_frame_->width() < input_frame_->height() ? input_frame_->width() | 156 q->PostTask([this] { TriggerScale(kScaleUp); }); |
177 : input_frame_->height(); | 157 WAIT_FOR_MS(50); |
178 int min_dimension = initial_min_dimension; | |
179 int current_shift = 0; | |
180 // Drop all frames to force-trigger downscaling. | |
181 while (min_dimension >= 2 * kMinDownscaleDimension) { | |
182 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " | |
183 << kNumSeconds << " seconds."; | |
184 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
185 QualityScaler::Resolution res = qs_.GetScaledResolution(); | |
186 min_dimension = res.width < res.height ? res.width : res.height; | |
187 ++current_shift; | |
188 ASSERT_EQ(input_frame_->width() >> current_shift, res.width); | |
189 ASSERT_EQ(input_frame_->height() >> current_shift, res.height); | |
190 ExpectScaleUsingReportedResolution(); | |
191 } | |
192 | |
193 // Make sure we can scale back with good-quality frames. | |
194 while (min_dimension < initial_min_dimension) { | |
195 EXPECT_TRUE(TriggerScale(kScaleUp)) << "No upscale within " << kNumSeconds | |
196 << " seconds."; | |
197 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
198 QualityScaler::Resolution res = qs_.GetScaledResolution(); | |
199 min_dimension = res.width < res.height ? res.width : res.height; | |
200 --current_shift; | |
201 ASSERT_EQ(input_frame_->width() >> current_shift, res.width); | |
202 ASSERT_EQ(input_frame_->height() >> current_shift, res.height); | |
203 ExpectScaleUsingReportedResolution(); | |
204 } | |
205 | |
206 // Verify we don't start upscaling after further low use. | |
207 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | |
208 qs_.ReportQP(kLowQp); | |
209 ExpectOriginalFrame(); | |
210 } | |
211 } | 158 } |
212 | 159 |
213 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { | 160 TEST_F(QualityScalerTest, ScalesDownAndBackUp) { |
214 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 161 { |
162 testing::InSequence _; | |
163 EXPECT_CALL(*observer_, ScaleDown(reason_)).Times(1); | |
164 EXPECT_CALL(*observer_, ScaleUp(reason_)).Times(1); | |
165 } | |
166 q->PostTask([this] { TriggerScale(kScaleDown); }); | |
167 WAIT_FOR_MS(50); | |
168 q->PostTask([this] { TriggerScale(kScaleUp); }); | |
169 WAIT_FOR_MS(50); | |
215 } | 170 } |
216 | 171 |
217 TEST_F(QualityScalerTest, | 172 #undef WAIT_FOR_MS |
218 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { | |
219 const int kOddWidth = 517; | |
220 const int kOddHeight = 1239; | |
221 input_frame_ = I420Buffer::Create(kOddWidth, kOddHeight); | |
222 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | |
223 } | |
224 | |
225 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { | |
226 input_frame_ = I420Buffer::Create(width, height); | |
227 | |
228 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | |
229 qs_.ReportDroppedFrame(); | |
230 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
231 ASSERT_EQ(input_frame_->width(), qs_.GetScaledResolution().width) | |
232 << "Unexpected scale of minimal-size frame."; | |
233 } | |
234 } | |
235 | |
236 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { | |
237 DoesNotDownscaleFrameDimensions(1, kHeight); | |
238 } | |
239 | |
240 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { | |
241 DoesNotDownscaleFrameDimensions(kWidth, 1); | |
242 } | |
243 | |
244 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { | |
245 DoesNotDownscaleFrameDimensions(1, 1); | |
246 } | |
247 | |
248 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { | |
249 DoesNotDownscaleFrameDimensions( | |
250 2 * kMinDownscaleDimension - 1, 1000); | |
251 } | |
252 | |
253 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { | |
254 DoesNotDownscaleFrameDimensions( | |
255 1000, 2 * kMinDownscaleDimension - 1); | |
256 } | |
257 | |
258 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { | |
259 static const int kWidth720p = 1280; | |
260 static const int kHeight720p = 720; | |
261 static const int kInitialBitrateKbps = 300; | |
262 input_frame_ = I420Buffer::Create(kWidth720p, kHeight720p); | |
263 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps, | |
264 kWidth720p, kHeight720p, kFramerate); | |
265 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
266 int init_width = qs_.GetScaledResolution().width; | |
267 int init_height = qs_.GetScaledResolution().height; | |
268 EXPECT_EQ(640, init_width); | |
269 EXPECT_EQ(360, init_height); | |
270 } | |
271 | |
272 TEST_F(QualityScalerTest, DownscaleToQvgaOnLowerInitialBitrate) { | |
273 static const int kWidth720p = 1280; | |
274 static const int kHeight720p = 720; | |
275 static const int kInitialBitrateKbps = 200; | |
276 input_frame_ = I420Buffer::Create(kWidth720p, kHeight720p); | |
277 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps, | |
278 kWidth720p, kHeight720p, kFramerate); | |
279 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
280 int init_width = qs_.GetScaledResolution().width; | |
281 int init_height = qs_.GetScaledResolution().height; | |
282 EXPECT_EQ(320, init_width); | |
283 EXPECT_EQ(180, init_height); | |
284 } | |
285 | |
286 TEST_F(QualityScalerTest, DownscaleAfterMeasuredSecondsThenSlowerBackUp) { | |
287 qs_.Init(kLowQpThreshold, kHighQp, 0, kWidth, kHeight, kFramerate); | |
288 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
289 QualityScaler::Resolution initial_res = qs_.GetScaledResolution(); | |
290 | |
291 // Should not downscale if less than kMeasureSecondsDownscale seconds passed. | |
292 for (int i = 0; i < kFramerate * kMeasureSecondsDownscale - 1; ++i) { | |
293 qs_.ReportQP(kHighQp + 1); | |
294 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
295 } | |
296 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | |
297 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | |
298 | |
299 // Should downscale if more than kMeasureSecondsDownscale seconds passed (add | |
300 // last frame). | |
301 qs_.ReportQP(kHighQp + 1); | |
302 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
303 EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width); | |
304 EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height); | |
305 | |
306 // Should not upscale if less than kMeasureSecondsUpscale seconds passed since | |
307 // we saw issues initially (have already gone down). | |
308 for (int i = 0; i < kFramerate * kMeasureSecondsUpscale - 1; ++i) { | |
309 qs_.ReportQP(kLowQp); | |
310 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
311 } | |
312 EXPECT_GT(initial_res.width, qs_.GetScaledResolution().width); | |
313 EXPECT_GT(initial_res.height, qs_.GetScaledResolution().height); | |
314 | |
315 // Should upscale (back to initial) if kMeasureSecondsUpscale seconds passed | |
316 // (add last frame). | |
317 qs_.ReportQP(kLowQp); | |
318 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
319 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | |
320 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | |
321 } | |
322 | |
323 TEST_F(QualityScalerTest, UpscaleQuicklyInitiallyAfterMeasuredSeconds) { | |
324 qs_.Init(kLowQpThreshold, kHighQp, kLowInitialBitrateKbps, kWidth, kHeight, | |
325 kFramerate); | |
326 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
327 QualityScaler::Resolution initial_res = qs_.GetScaledResolution(); | |
328 | |
329 // Should not upscale if less than kMeasureSecondsFastUpscale seconds passed. | |
330 for (int i = 0; i < kFramerate * kMeasureSecondsFastUpscale - 1; ++i) { | |
331 qs_.ReportQP(kLowQp); | |
332 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
333 } | |
334 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | |
335 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | |
336 | |
337 // Should upscale if kMeasureSecondsFastUpscale seconds passed (add last | |
338 // frame). | |
339 qs_.ReportQP(kLowQp); | |
340 qs_.OnEncodeFrame(input_frame_->width(), input_frame_->height()); | |
341 EXPECT_LT(initial_res.width, qs_.GetScaledResolution().width); | |
342 EXPECT_LT(initial_res.height, qs_.GetScaledResolution().height); | |
343 } | |
344 | |
345 void QualityScalerTest::DownscaleEndsAt(int input_width, | |
346 int input_height, | |
347 int end_width, | |
348 int end_height) { | |
349 // Create a frame with 2x expected end width/height to verify that we can | |
350 // scale down to expected end width/height. | |
351 input_frame_ = I420Buffer::Create(input_width, input_height); | |
352 | |
353 int last_width = input_width; | |
354 int last_height = input_height; | |
355 // Drop all frames to force-trigger downscaling. | |
356 while (true) { | |
357 TriggerScale(kScaleDown); | |
358 QualityScaler::Resolution res = qs_.GetScaledResolution(); | |
359 if (last_width == res.width) { | |
360 EXPECT_EQ(last_height, res.height); | |
361 EXPECT_EQ(end_width, res.width); | |
362 EXPECT_EQ(end_height, res.height); | |
363 break; | |
364 } | |
365 last_width = res.width; | |
366 last_height = res.height; | |
367 } | |
368 } | |
369 | |
370 TEST_F(QualityScalerTest, DownscalesTo320x180) { | |
371 DownscaleEndsAt(640, 360, 320, 180); | |
372 } | |
373 | |
374 TEST_F(QualityScalerTest, DownscalesTo180x320) { | |
375 DownscaleEndsAt(360, 640, 180, 320); | |
376 } | |
377 | |
378 TEST_F(QualityScalerTest, DownscalesFrom1280x720To320x180) { | |
379 DownscaleEndsAt(1280, 720, 320, 180); | |
380 } | |
381 | |
382 TEST_F(QualityScalerTest, DoesntDownscaleInitialQvga) { | |
383 DownscaleEndsAt(320, 180, 320, 180); | |
384 } | |
385 | 173 |
386 } // namespace webrtc | 174 } // namespace webrtc |
OLD | NEW |