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 16 matching lines...) Expand all Loading... |
27 static const int kLowInitialBitrateKbps = 300; | 27 static const int kLowInitialBitrateKbps = 300; |
28 // These values need to be in sync with corresponding constants | 28 // These values need to be in sync with corresponding constants |
29 // in quality_scaler.cc | 29 // in quality_scaler.cc |
30 static const int kMeasureSecondsFastUpscale = 2; | 30 static const int kMeasureSecondsFastUpscale = 2; |
31 static const int kMeasureSecondsUpscale = 5; | 31 static const int kMeasureSecondsUpscale = 5; |
32 static const int kMeasureSecondsDownscale = 5; | 32 static const int kMeasureSecondsDownscale = 5; |
33 static const int kMinDownscaleDimension = 140; | 33 static const int kMinDownscaleDimension = 140; |
34 } // namespace | 34 } // namespace |
35 | 35 |
36 class QualityScalerTest : public ::testing::Test { | 36 class QualityScalerTest : public ::testing::Test { |
37 public: | |
38 // Temporal and spatial resolution. | |
39 struct Resolution { | |
40 int framerate; | |
41 int width; | |
42 int height; | |
43 }; | |
44 | |
45 protected: | 37 protected: |
46 enum ScaleDirection { | 38 enum ScaleDirection { |
47 kKeepScaleAtHighQp, | 39 kKeepScaleAtHighQp, |
48 kScaleDown, | 40 kScaleDown, |
49 kScaleDownAboveHighQp, | 41 kScaleDownAboveHighQp, |
50 kScaleUp | 42 kScaleUp |
51 }; | 43 }; |
52 enum BadQualityMetric { kDropFrame, kReportLowQP }; | |
53 | 44 |
54 QualityScalerTest() { | 45 QualityScalerTest() { |
55 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, | 46 input_frame_.CreateEmptyFrame(kWidth, kHeight, kWidth, kHalfWidth, |
56 kHalfWidth); | 47 kHalfWidth); |
57 qs_.Init(kLowQpThreshold, kHighQp, false, 0, 0, 0, kFramerate); | 48 qs_.Init(kLowQpThreshold, kHighQp, 0, 0, 0, kFramerate); |
58 qs_.OnEncodeFrame(input_frame_); | 49 qs_.OnEncodeFrame(input_frame_); |
59 } | 50 } |
60 | 51 |
61 bool TriggerScale(ScaleDirection scale_direction) { | 52 bool TriggerScale(ScaleDirection scale_direction) { |
62 qs_.OnEncodeFrame(input_frame_); | 53 qs_.OnEncodeFrame(input_frame_); |
63 int initial_width = qs_.GetScaledResolution().width; | 54 int initial_width = qs_.GetScaledResolution().width; |
64 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { | 55 for (int i = 0; i < kFramerate * kNumSeconds; ++i) { |
65 switch (scale_direction) { | 56 switch (scale_direction) { |
66 case kScaleUp: | 57 case kScaleUp: |
67 qs_.ReportQP(kLowQp); | 58 qs_.ReportQP(kLowQp); |
(...skipping 26 matching lines...) Expand all Loading... |
94 QualityScaler::Resolution res = qs_.GetScaledResolution(); | 85 QualityScaler::Resolution res = qs_.GetScaledResolution(); |
95 const VideoFrame& scaled_frame = qs_.GetScaledFrame(input_frame_); | 86 const VideoFrame& scaled_frame = qs_.GetScaledFrame(input_frame_); |
96 EXPECT_EQ(res.width, scaled_frame.width()); | 87 EXPECT_EQ(res.width, scaled_frame.width()); |
97 EXPECT_EQ(res.height, scaled_frame.height()); | 88 EXPECT_EQ(res.height, scaled_frame.height()); |
98 } | 89 } |
99 | 90 |
100 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); | 91 void ContinuouslyDownscalesByHalfDimensionsAndBackUp(); |
101 | 92 |
102 void DoesNotDownscaleFrameDimensions(int width, int height); | 93 void DoesNotDownscaleFrameDimensions(int width, int height); |
103 | 94 |
104 Resolution TriggerResolutionChange(BadQualityMetric dropframe_lowqp, | |
105 int num_second, | |
106 int initial_framerate); | |
107 | |
108 void VerifyQualityAdaptation(int initial_framerate, | |
109 int seconds_downscale, | |
110 int seconds_upscale, | |
111 bool expect_spatial_resize, | |
112 bool expect_framerate_reduction); | |
113 | |
114 void DownscaleEndsAt(int input_width, | 95 void DownscaleEndsAt(int input_width, |
115 int input_height, | 96 int input_height, |
116 int end_width, | 97 int end_width, |
117 int end_height); | 98 int end_height); |
118 | 99 |
119 QualityScaler qs_; | 100 QualityScaler qs_; |
120 VideoFrame input_frame_; | 101 VideoFrame input_frame_; |
121 }; | 102 }; |
122 | 103 |
123 TEST_F(QualityScalerTest, UsesOriginalFrameInitially) { | 104 TEST_F(QualityScalerTest, UsesOriginalFrameInitially) { |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 } | 242 } |
262 | 243 |
263 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { | 244 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1PxHeight) { |
264 DoesNotDownscaleFrameDimensions(kWidth, 1); | 245 DoesNotDownscaleFrameDimensions(kWidth, 1); |
265 } | 246 } |
266 | 247 |
267 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { | 248 TEST_F(QualityScalerTest, DoesNotDownscaleFrom1Px) { |
268 DoesNotDownscaleFrameDimensions(1, 1); | 249 DoesNotDownscaleFrameDimensions(1, 1); |
269 } | 250 } |
270 | 251 |
271 QualityScalerTest::Resolution QualityScalerTest::TriggerResolutionChange( | |
272 BadQualityMetric dropframe_lowqp, | |
273 int num_second, | |
274 int initial_framerate) { | |
275 QualityScalerTest::Resolution res; | |
276 res.framerate = initial_framerate; | |
277 qs_.OnEncodeFrame(input_frame_); | |
278 res.width = qs_.GetScaledResolution().width; | |
279 res.height = qs_.GetScaledResolution().height; | |
280 for (int i = 0; i < kFramerate * num_second; ++i) { | |
281 switch (dropframe_lowqp) { | |
282 case kReportLowQP: | |
283 qs_.ReportQP(kLowQp); | |
284 break; | |
285 case kDropFrame: | |
286 qs_.ReportDroppedFrame(); | |
287 break; | |
288 } | |
289 qs_.OnEncodeFrame(input_frame_); | |
290 // Simulate the case when SetRates is called right after reducing | |
291 // framerate. | |
292 qs_.ReportFramerate(initial_framerate); | |
293 res.framerate = qs_.GetTargetFramerate(); | |
294 if (res.framerate != -1) | |
295 qs_.ReportFramerate(res.framerate); | |
296 res.width = qs_.GetScaledResolution().width; | |
297 res.height = qs_.GetScaledResolution().height; | |
298 } | |
299 return res; | |
300 } | |
301 | |
302 void QualityScalerTest::VerifyQualityAdaptation( | |
303 int initial_framerate, | |
304 int seconds_downscale, | |
305 int seconds_upscale, | |
306 bool expect_spatial_resize, | |
307 bool expect_framerate_reduction) { | |
308 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, true, 0, 0, 0, | |
309 initial_framerate); | |
310 qs_.OnEncodeFrame(input_frame_); | |
311 int init_width = qs_.GetScaledResolution().width; | |
312 int init_height = qs_.GetScaledResolution().height; | |
313 | |
314 // Test reducing framerate by dropping frame continuously. | |
315 QualityScalerTest::Resolution res = | |
316 TriggerResolutionChange(kDropFrame, seconds_downscale, initial_framerate); | |
317 | |
318 if (expect_framerate_reduction) { | |
319 EXPECT_LT(res.framerate, initial_framerate); | |
320 } else { | |
321 // No framerate reduction, video decimator should be disabled. | |
322 EXPECT_EQ(-1, res.framerate); | |
323 } | |
324 | |
325 if (expect_spatial_resize) { | |
326 EXPECT_LT(res.width, init_width); | |
327 EXPECT_LT(res.height, init_height); | |
328 } else { | |
329 EXPECT_EQ(init_width, res.width); | |
330 EXPECT_EQ(init_height, res.height); | |
331 } | |
332 | |
333 // The "seconds * 1.5" is to ensure spatial resolution to recover. | |
334 // For example, in 6 seconds test, framerate reduction happens in the first | |
335 // 3 seconds from 30fps to 15fps and causes the buffer size to be half of the | |
336 // original one. Then it will take only 45 samples to downscale (twice in 90 | |
337 // samples). So to recover the resolution changes, we need more than 10 | |
338 // seconds (i.e, seconds_upscale * 1.5). This is because the framerate | |
339 // increases before spatial size recovers, so it will take 150 samples to | |
340 // recover spatial size (300 for twice). | |
341 res = TriggerResolutionChange(kReportLowQP, seconds_upscale * 1.5, | |
342 initial_framerate); | |
343 EXPECT_EQ(-1, res.framerate); | |
344 EXPECT_EQ(init_width, res.width); | |
345 EXPECT_EQ(init_height, res.height); | |
346 } | |
347 | |
348 // In 3 seconds test, only framerate adjusting should happen and 5 second | |
349 // upscaling duration, only a framerate adjusting should happen. | |
350 TEST_F(QualityScalerTest, ChangeFramerateOnly) { | |
351 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale, | |
352 kMeasureSecondsUpscale, false, true); | |
353 } | |
354 | |
355 // In 6 seconds test, framerate adjusting and scaling are both | |
356 // triggered, it shows that scaling would happen after framerate | |
357 // adjusting. | |
358 TEST_F(QualityScalerTest, ChangeFramerateAndSpatialSize) { | |
359 VerifyQualityAdaptation(kFramerate, kMeasureSecondsDownscale * 2, | |
360 kMeasureSecondsUpscale * 2, true, true); | |
361 } | |
362 | |
363 // When starting from a low framerate, only spatial size will be changed. | |
364 TEST_F(QualityScalerTest, ChangeSpatialSizeOnly) { | |
365 qs_.ReportFramerate(kFramerate >> 1); | |
366 VerifyQualityAdaptation(kFramerate >> 1, kMeasureSecondsDownscale * 2, | |
367 kMeasureSecondsUpscale * 2, true, false); | |
368 } | |
369 | |
370 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { | 252 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsWidth) { |
371 DoesNotDownscaleFrameDimensions( | 253 DoesNotDownscaleFrameDimensions( |
372 2 * kMinDownscaleDimension - 1, 1000); | 254 2 * kMinDownscaleDimension - 1, 1000); |
373 } | 255 } |
374 | 256 |
375 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { | 257 TEST_F(QualityScalerTest, DoesNotDownscaleBelow2xDefaultMinDimensionsHeight) { |
376 DoesNotDownscaleFrameDimensions( | 258 DoesNotDownscaleFrameDimensions( |
377 1000, 2 * kMinDownscaleDimension - 1); | 259 1000, 2 * kMinDownscaleDimension - 1); |
378 } | 260 } |
379 | 261 |
380 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { | 262 TEST_F(QualityScalerTest, DownscaleToVgaOnLowInitialBitrate) { |
381 static const int kWidth720p = 1280; | 263 static const int kWidth720p = 1280; |
382 static const int kHeight720p = 720; | 264 static const int kHeight720p = 720; |
383 static const int kInitialBitrateKbps = 300; | 265 static const int kInitialBitrateKbps = 300; |
384 input_frame_.CreateEmptyFrame(kWidth720p, kHeight720p, kWidth720p, | 266 input_frame_.CreateEmptyFrame(kWidth720p, kHeight720p, kWidth720p, |
385 kWidth720p / 2, kWidth720p / 2); | 267 kWidth720p / 2, kWidth720p / 2); |
386 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, true, kInitialBitrateKbps, | 268 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps, |
387 kWidth720p, kHeight720p, kFramerate); | 269 kWidth720p, kHeight720p, kFramerate); |
388 qs_.OnEncodeFrame(input_frame_); | 270 qs_.OnEncodeFrame(input_frame_); |
389 int init_width = qs_.GetScaledResolution().width; | 271 int init_width = qs_.GetScaledResolution().width; |
390 int init_height = qs_.GetScaledResolution().height; | 272 int init_height = qs_.GetScaledResolution().height; |
391 EXPECT_EQ(640, init_width); | 273 EXPECT_EQ(640, init_width); |
392 EXPECT_EQ(360, init_height); | 274 EXPECT_EQ(360, init_height); |
393 } | 275 } |
394 | 276 |
395 TEST_F(QualityScalerTest, DownscaleToQvgaOnLowerInitialBitrate) { | 277 TEST_F(QualityScalerTest, DownscaleToQvgaOnLowerInitialBitrate) { |
396 static const int kWidth720p = 1280; | 278 static const int kWidth720p = 1280; |
397 static const int kHeight720p = 720; | 279 static const int kHeight720p = 720; |
398 static const int kInitialBitrateKbps = 200; | 280 static const int kInitialBitrateKbps = 200; |
399 input_frame_.CreateEmptyFrame(kWidth720p, kHeight720p, kWidth720p, | 281 input_frame_.CreateEmptyFrame(kWidth720p, kHeight720p, kWidth720p, |
400 kWidth720p / 2, kWidth720p / 2); | 282 kWidth720p / 2, kWidth720p / 2); |
401 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, true, kInitialBitrateKbps, | 283 qs_.Init(kLowQpThreshold, kDisabledBadQpThreshold, kInitialBitrateKbps, |
402 kWidth720p, kHeight720p, kFramerate); | 284 kWidth720p, kHeight720p, kFramerate); |
403 qs_.OnEncodeFrame(input_frame_); | 285 qs_.OnEncodeFrame(input_frame_); |
404 int init_width = qs_.GetScaledResolution().width; | 286 int init_width = qs_.GetScaledResolution().width; |
405 int init_height = qs_.GetScaledResolution().height; | 287 int init_height = qs_.GetScaledResolution().height; |
406 EXPECT_EQ(320, init_width); | 288 EXPECT_EQ(320, init_width); |
407 EXPECT_EQ(180, init_height); | 289 EXPECT_EQ(180, init_height); |
408 } | 290 } |
409 | 291 |
410 TEST_F(QualityScalerTest, DownscaleAfterMeasuredSecondsThenSlowerBackUp) { | 292 TEST_F(QualityScalerTest, DownscaleAfterMeasuredSecondsThenSlowerBackUp) { |
411 QualityScalerTest::Resolution initial_res; | 293 qs_.Init(kLowQpThreshold, kHighQp, 0, kWidth, kHeight, kFramerate); |
412 qs_.Init(kLowQpThreshold, kHighQp, false, 0, kWidth, kHeight, kFramerate); | |
413 qs_.OnEncodeFrame(input_frame_); | 294 qs_.OnEncodeFrame(input_frame_); |
414 initial_res.width = qs_.GetScaledResolution().width; | 295 QualityScaler::Resolution initial_res = qs_.GetScaledResolution(); |
415 initial_res.height = qs_.GetScaledResolution().height; | |
416 | 296 |
417 // Should not downscale if less than kMeasureSecondsDownscale seconds passed. | 297 // Should not downscale if less than kMeasureSecondsDownscale seconds passed. |
418 for (int i = 0; i < kFramerate * kMeasureSecondsDownscale - 1; ++i) { | 298 for (int i = 0; i < kFramerate * kMeasureSecondsDownscale - 1; ++i) { |
419 qs_.ReportQP(kHighQp + 1); | 299 qs_.ReportQP(kHighQp + 1); |
420 qs_.OnEncodeFrame(input_frame_); | 300 qs_.OnEncodeFrame(input_frame_); |
421 } | 301 } |
422 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | 302 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
423 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | 303 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
424 | 304 |
425 // Should downscale if more than kMeasureSecondsDownscale seconds passed (add | 305 // Should downscale if more than kMeasureSecondsDownscale seconds passed (add |
(...skipping 14 matching lines...) Expand all Loading... |
440 | 320 |
441 // Should upscale (back to initial) if kMeasureSecondsUpscale seconds passed | 321 // Should upscale (back to initial) if kMeasureSecondsUpscale seconds passed |
442 // (add last frame). | 322 // (add last frame). |
443 qs_.ReportQP(kLowQp); | 323 qs_.ReportQP(kLowQp); |
444 qs_.OnEncodeFrame(input_frame_); | 324 qs_.OnEncodeFrame(input_frame_); |
445 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | 325 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
446 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | 326 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
447 } | 327 } |
448 | 328 |
449 TEST_F(QualityScalerTest, UpscaleQuicklyInitiallyAfterMeasuredSeconds) { | 329 TEST_F(QualityScalerTest, UpscaleQuicklyInitiallyAfterMeasuredSeconds) { |
450 QualityScalerTest::Resolution initial_res; | 330 qs_.Init(kLowQpThreshold, kHighQp, kLowInitialBitrateKbps, kWidth, kHeight, |
451 qs_.Init(kLowQpThreshold, kHighQp, false, kLowInitialBitrateKbps, kWidth, | 331 kFramerate); |
452 kHeight, kFramerate); | |
453 qs_.OnEncodeFrame(input_frame_); | 332 qs_.OnEncodeFrame(input_frame_); |
454 initial_res.width = qs_.GetScaledResolution().width; | 333 QualityScaler::Resolution initial_res = qs_.GetScaledResolution(); |
455 initial_res.height = qs_.GetScaledResolution().height; | |
456 | 334 |
457 // Should not upscale if less than kMeasureSecondsFastUpscale seconds passed. | 335 // Should not upscale if less than kMeasureSecondsFastUpscale seconds passed. |
458 for (int i = 0; i < kFramerate * kMeasureSecondsFastUpscale - 1; ++i) { | 336 for (int i = 0; i < kFramerate * kMeasureSecondsFastUpscale - 1; ++i) { |
459 qs_.ReportQP(kLowQp); | 337 qs_.ReportQP(kLowQp); |
460 qs_.OnEncodeFrame(input_frame_); | 338 qs_.OnEncodeFrame(input_frame_); |
461 } | 339 } |
462 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); | 340 EXPECT_EQ(initial_res.width, qs_.GetScaledResolution().width); |
463 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); | 341 EXPECT_EQ(initial_res.height, qs_.GetScaledResolution().height); |
464 | 342 |
465 // Should upscale if kMeasureSecondsFastUpscale seconds passed (add last | 343 // Should upscale if kMeasureSecondsFastUpscale seconds passed (add last |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 | 384 |
507 TEST_F(QualityScalerTest, DownscalesFrom1280x720To320x180) { | 385 TEST_F(QualityScalerTest, DownscalesFrom1280x720To320x180) { |
508 DownscaleEndsAt(1280, 720, 320, 180); | 386 DownscaleEndsAt(1280, 720, 320, 180); |
509 } | 387 } |
510 | 388 |
511 TEST_F(QualityScalerTest, DoesntDownscaleInitialQvga) { | 389 TEST_F(QualityScalerTest, DoesntDownscaleInitialQvga) { |
512 DownscaleEndsAt(320, 180, 320, 180); | 390 DownscaleEndsAt(320, 180, 320, 180); |
513 } | 391 } |
514 | 392 |
515 } // namespace webrtc | 393 } // namespace webrtc |
OLD | NEW |