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 |
(...skipping 15 matching lines...) Expand all Loading... |
26 } // namespace | 26 } // namespace |
27 | 27 |
28 class QualityScalerTest : public ::testing::Test { | 28 class QualityScalerTest : public ::testing::Test { |
29 public: | 29 public: |
30 // Temporal and spatial resolution. | 30 // Temporal and spatial resolution. |
31 struct Resolution { | 31 struct Resolution { |
32 int framerate; | 32 int framerate; |
33 int width; | 33 int width; |
34 int height; | 34 int height; |
35 }; | 35 }; |
| 36 |
36 protected: | 37 protected: |
37 enum ScaleDirection { | 38 enum ScaleDirection { |
38 kKeepScaleAtHighQp, | 39 kKeepScaleAtHighQp, |
39 kScaleDown, | 40 kScaleDown, |
40 kScaleDownAboveHighQp, | 41 kScaleDownAboveHighQp, |
41 kScaleUp | 42 kScaleUp |
42 }; | 43 }; |
43 enum BadQualityMetric { kDropFrame, kReportLowQP }; | 44 enum BadQualityMetric { kDropFrame, kReportLowQP }; |
44 | 45 |
45 QualityScalerTest() { | 46 QualityScalerTest() { |
46 input_frame_.CreateEmptyFrame( | 47 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, |
47 kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth); | 48 kHalfWidth); |
48 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false); | 49 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, kHighQp, false); |
49 qs_.ReportFramerate(kFramerate); | 50 qs_.ReportFramerate(kFramerate); |
50 qs_.OnEncodeFrame(input_frame_); | 51 qs_.OnEncodeFrame(input_frame_); |
51 } | 52 } |
52 | 53 |
53 bool TriggerScale(ScaleDirection scale_direction) { | 54 bool TriggerScale(ScaleDirection scale_direction) { |
54 qs_.OnEncodeFrame(input_frame_); | 55 qs_.OnEncodeFrame(input_frame_); |
55 int initial_width = qs_.GetScaledResolution().width; | 56 int initial_width = qs_.GetScaledResolution().width; |
56 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 57 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
57 switch (scale_direction) { | 58 switch (scale_direction) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 } | 91 } |
91 | 92 |
92 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 93 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
93 | 94 |
94 void DoesNotDownscaleFrameDimensions(int width, int height); | 95 void DoesNotDownscaleFrameDimensions(int width, int height); |
95 | 96 |
96 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, | 97 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, |
97 int num_second, | 98 int num_second, |
98 int initial_framerate); | 99 int initial_framerate); |
99 | 100 |
100 void VerifyQualityAdaptation(int initial_framerate, int seconds, | 101 void VerifyQualityAdaptation(int initial_framerate, |
| 102 int seconds, |
101 bool expect_spatial_resize, | 103 bool expect_spatial_resize, |
102 bool expect_framerate_reduction); | 104 bool expect_framerate_reduction); |
103 | 105 |
104 void DownscaleEndsAt(int input_width, | 106 void DownscaleEndsAt(int input_width, |
105 int input_height, | 107 int input_height, |
106 int end_width, | 108 int end_width, |
107 int end_height); | 109 int end_height); |
108 | 110 |
109 QualityScaler qs_; | 111 QualityScaler qs_; |
110 VideoFrame input_frame_; | 112 VideoFrame input_frame_; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 | 178 |
177 qs_.ReportDroppedFrame(); | 179 qs_.ReportDroppedFrame(); |
178 qs_.OnEncodeFrame(input_frame_); | 180 qs_.OnEncodeFrame(input_frame_); |
179 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) | 181 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) |
180 << "Unexpected scale on half framedrop."; | 182 << "Unexpected scale on half framedrop."; |
181 } | 183 } |
182 } | 184 } |
183 | 185 |
184 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { | 186 void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { |
185 const int initial_min_dimension = input_frame_.width() < input_frame_.height() | 187 const int initial_min_dimension = input_frame_.width() < input_frame_.height() |
186 ? input_frame_.width() | 188 ? input_frame_.width() |
187 : input_frame_.height(); | 189 : input_frame_.height(); |
188 int min_dimension = initial_min_dimension; | 190 int min_dimension = initial_min_dimension; |
189 int current_shift = 0; | 191 int current_shift = 0; |
190 // Drop all frames to force-trigger downscaling. | 192 // Drop all frames to force-trigger downscaling. |
191 while (min_dimension >= 2 * QualityScaler::kDefaultMinDownscaleDimension) { | 193 while (min_dimension >= 2 * QualityScaler::kDefaultMinDownscaleDimension) { |
192 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " | 194 EXPECT_TRUE(TriggerScale(kScaleDown)) << "No downscale within " |
193 << kNumSeconds << " seconds."; | 195 << kNumSeconds << " seconds."; |
194 qs_.OnEncodeFrame(input_frame_); | 196 qs_.OnEncodeFrame(input_frame_); |
195 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 197 QualityScaler::Resolution res = qs_.GetScaledResolution(); |
196 min_dimension = res.width < res.height ? res.width : res.height; | 198 min_dimension = res.width < res.height ? res.width : res.height; |
197 ++current_shift; | 199 ++current_shift; |
(...skipping 24 matching lines...) Expand all Loading... |
222 | 224 |
223 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { | 225 TEST_F(QualityScalerTest, ContinuouslyDownscalesByHalfDimensionsAndBackUp) { |
224 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 226 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
225 } | 227 } |
226 | 228 |
227 TEST_F(QualityScalerTest, | 229 TEST_F(QualityScalerTest, |
228 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { | 230 ContinuouslyDownscalesOddResolutionsByHalfDimensionsAndBackUp) { |
229 const int kOddWidth = 517; | 231 const int kOddWidth = 517; |
230 const int kHalfOddWidth = (kOddWidth + 1) / 2; | 232 const int kHalfOddWidth = (kOddWidth + 1) / 2; |
231 const int kOddHeight = 1239; | 233 const int kOddHeight = 1239; |
232 input_frame_.CreateEmptyFrame( | 234 input_frame_.CreateEmptyFrame(kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, |
233 kOddWidth, kOddHeight, kOddWidth, kHalfOddWidth, kHalfOddWidth); | 235 kHalfOddWidth); |
234 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 236 ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
235 } | 237 } |
236 | 238 |
237 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { | 239 void QualityScalerTest::DoesNotDownscaleFrameDimensions(int width, int height) { |
238 input_frame_.CreateEmptyFrame( | 240 input_frame_.CreateEmptyFrame(width, height, width, (width + 1) / 2, |
239 width, height, width, (width + 1) / 2, (width + 1) / 2); | 241 (width + 1) / 2); |
240 | 242 |
241 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 243 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
242 qs_.ReportDroppedFrame(); | 244 qs_.ReportDroppedFrame(); |
243 qs_.OnEncodeFrame(input_frame_); | 245 qs_.OnEncodeFrame(input_frame_); |
244 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) | 246 ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution().width) |
245 << "Unexpected scale of minimal-size frame."; | 247 << "Unexpected scale of minimal-size frame."; |
246 } | 248 } |
247 } | 249 } |
248 | 250 |
249 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { | 251 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxWidth) { |
250 DoesNotDownscaleFrameDimensions(1, kHeight); | 252 DoesNotDownscaleFrameDimensions(1, kHeight); |
251 } | 253 } |
252 | 254 |
253 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { | 255 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { |
254 DoesNotDownscaleFrameDimensions(kWidth, 1); | 256 DoesNotDownscaleFrameDimensions(kWidth, 1); |
255 } | 257 } |
256 | 258 |
257 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { | 259 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { |
258 DoesNotDownscaleFrameDimensions(1, 1); | 260 DoesNotDownscaleFrameDimensions(1, 1); |
259 } | 261 } |
260 | 262 |
261 QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange( | 263 QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange( |
262 BadQualityMetric dropframe_lowqp, int num_second, int initial_framerate) { | 264 BadQualityMetric dropframe_lowqp, |
| 265 int num_second, |
| 266 int initial_framerate) { |
263 QualityScalerTest::Resolution res; | 267 QualityScalerTest::Resolution res; |
264 res.framerate = initial_framerate; | 268 res.framerate = initial_framerate; |
265 qs_.OnEncodeFrame(input_frame_); | 269 qs_.OnEncodeFrame(input_frame_); |
266 res.width = qs_.GetScaledResolution().width; | 270 res.width = qs_.GetScaledResolution().width; |
267 res.height = qs_.GetScaledResolution().height; | 271 res.height = qs_.GetScaledResolution().height; |
268 for (int i = 0; i < kFramerate * num_second; ++i) { | 272 for (int i = 0; i < kFramerate * num_second; ++i) { |
269 switch (dropframe_lowqp) { | 273 switch (dropframe_lowqp) { |
270 case kReportLowQP: | 274 case kReportLowQP: |
271 qs_.ReportQP(kLowQp); | 275 qs_.ReportQP(kLowQp); |
272 break; | 276 break; |
273 case kDropFrame: | 277 case kDropFrame: |
274 qs_.ReportDroppedFrame(); | 278 qs_.ReportDroppedFrame(); |
275 break; | 279 break; |
276 } | 280 } |
277 qs_.OnEncodeFrame(input_frame_); | 281 qs_.OnEncodeFrame(input_frame_); |
278 // Simulate the case when SetRates is called right after reducing | 282 // Simulate the case when SetRates is called right after reducing |
279 // framerate. | 283 // framerate. |
280 qs_.ReportFramerate(initial_framerate); | 284 qs_.ReportFramerate(initial_framerate); |
281 res.framerate = qs_.GetTargetFramerate(); | 285 res.framerate = qs_.GetTargetFramerate(); |
282 if (res.framerate != -1) | 286 if (res.framerate != -1) |
283 qs_.ReportFramerate(res.framerate); | 287 qs_.ReportFramerate(res.framerate); |
284 res.width = qs_.GetScaledResolution().width; | 288 res.width = qs_.GetScaledResolution().width; |
285 res.height = qs_.GetScaledResolution().height; | 289 res.height = qs_.GetScaledResolution().height; |
286 } | 290 } |
287 return res; | 291 return res; |
288 } | 292 } |
289 | 293 |
290 void QualityScalerTest::VerifyQualityAdaptation( | 294 void QualityScalerTest::VerifyQualityAdaptation( |
291 int initial_framerate, int seconds, bool expect_spatial_resize, | 295 int initial_framerate, |
| 296 int seconds, |
| 297 bool expect_spatial_resize, |
292 bool expect_framerate_reduction) { | 298 bool expect_framerate_reduction) { |
293 const int kDisabledBadQpThreshold = kMaxQp + 1; | 299 const int kDisabledBadQpThreshold = kMaxQp + 1; |
294 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, | 300 qs_.Init(kMaxQp / QualityScaler::kDefaultLowQpDenominator, |
295 kDisabledBadQpThreshold, true); | 301 kDisabledBadQpThreshold, true); |
296 qs_.OnEncodeFrame(input_frame_); | 302 qs_.OnEncodeFrame(input_frame_); |
297 int init_width = qs_.GetScaledResolution().width; | 303 int init_width = qs_.GetScaledResolution().width; |
298 int init_height = qs_.GetScaledResolution().height; | 304 int init_height = qs_.GetScaledResolution().height; |
299 | 305 |
300 // Test reducing framerate by dropping frame continuously. | 306 // Test reducing framerate by dropping frame continuously. |
301 QualityScalerTest::Resolution res = TriggerResolutionChange( | 307 QualityScalerTest::Resolution res = |
302 kDropFrame, seconds, initial_framerate); | 308 TriggerResolutionChange(kDropFrame, seconds, initial_framerate); |
303 | 309 |
304 if (expect_framerate_reduction) { | 310 if (expect_framerate_reduction) { |
305 EXPECT_LT(res.framerate, initial_framerate); | 311 EXPECT_LT(res.framerate, initial_framerate); |
306 } else { | 312 } else { |
307 // No framerate reduction, video decimator should be disabled. | 313 // No framerate reduction, video decimator should be disabled. |
308 EXPECT_EQ(-1, res.framerate); | 314 EXPECT_EQ(-1, res.framerate); |
309 } | 315 } |
310 | 316 |
311 if (expect_spatial_resize) { | 317 if (expect_spatial_resize) { |
312 EXPECT_LT(res.width, init_width); | 318 EXPECT_LT(res.width, init_width); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 DownscaleEndsAt(1600, 800, 200, 100); | 416 DownscaleEndsAt(1600, 800, 200, 100); |
411 } | 417 } |
412 | 418 |
413 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { | 419 TEST_F(QualityScalerTest, RespectsMinResolutionHeight) { |
414 // Should end at 100x200, as height can't go lower. | 420 // Should end at 100x200, as height can't go lower. |
415 qs_.SetMinResolution(10, 200); | 421 qs_.SetMinResolution(10, 200); |
416 DownscaleEndsAt(800, 1600, 100, 200); | 422 DownscaleEndsAt(800, 1600, 100, 200); |
417 } | 423 } |
418 | 424 |
419 } // namespace webrtc | 425 } // namespace webrtc |
OLD | NEW |