blob: 3f393df91a9a381d390fa53d276136fbded20402 [file] [log] [blame]
Erik Språng7ca375c2019-02-06 16:20:17 +01001/*
2 * Copyright (c) 2019 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 "video/encoder_overshoot_detector.h"
12#include "api/units/data_rate.h"
13#include "rtc_base/fake_clock.h"
14#include "rtc_base/time_utils.h"
15#include "test/gtest.h"
16
17namespace webrtc {
18
19class EncoderOvershootDetectorTest : public ::testing::Test {
20 public:
21 static constexpr int kDefaultBitrateBps = 300000;
22 static constexpr double kDefaultFrameRateFps = 15;
23 EncoderOvershootDetectorTest()
24 : detector_(kWindowSizeMs),
25 target_bitrate_(DataRate::bps(kDefaultBitrateBps)),
26 target_framerate_fps_(kDefaultFrameRateFps) {}
27
28 protected:
29 void RunConstantUtilizationTest(double actual_utilization_factor,
30 double expected_utilization_factor,
31 double allowed_error,
32 int64_t test_duration_ms) {
33 const int frame_size_bytes =
34 static_cast<int>(actual_utilization_factor *
35 (target_bitrate_.bps() / target_framerate_fps_) / 8);
36 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
37 rtc::TimeMillis());
38
39 if (rtc::TimeMillis() == 0) {
40 // Encode a first frame which by definition has no overuse factor.
41 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
42 clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec /
43 target_framerate_fps_);
44 }
45
46 int64_t runtime_us = 0;
47 while (runtime_us < test_duration_ms * 1000) {
48 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
49 runtime_us += rtc::kNumMicrosecsPerSec / target_framerate_fps_;
50 clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec /
51 target_framerate_fps_);
52 }
53
54 absl::optional<double> utilization_factor =
55 detector_.GetUtilizationFactor(rtc::TimeMillis());
56 EXPECT_NEAR(utilization_factor.value_or(-1), expected_utilization_factor,
57 allowed_error);
58 }
59
60 static constexpr int64_t kWindowSizeMs = 3000;
61 EncoderOvershootDetector detector_;
62 rtc::ScopedFakeClock clock_;
63 DataRate target_bitrate_;
64 double target_framerate_fps_;
65};
66
67TEST_F(EncoderOvershootDetectorTest, NoUtilizationIfNoRate) {
68 const int frame_size_bytes = 1000;
69 const int64_t time_interval_ms = 33;
70 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
71 rtc::TimeMillis());
72
73 // No data points, can't determine overshoot rate.
74 EXPECT_FALSE(detector_.GetUtilizationFactor(rtc::TimeMillis()).has_value());
75
76 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
77 clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerMillisec * time_interval_ms);
78 EXPECT_TRUE(detector_.GetUtilizationFactor(rtc::TimeMillis()).has_value());
79}
80
81TEST_F(EncoderOvershootDetectorTest, OptimalSize) {
82 // Optimally behaved encoder.
83 // Allow some error margin due to rounding errors, eg due to frame
84 // interval not being an integer.
85 RunConstantUtilizationTest(1.0, 1.0, 0.01, kWindowSizeMs);
86}
87
88TEST_F(EncoderOvershootDetectorTest, Undershoot) {
89 // Undershoot, reported utilization factor should be capped to 1.0 so
90 // that we don't incorrectly boost encoder bitrate during movement.
91 RunConstantUtilizationTest(0.5, 1.0, 0.00, kWindowSizeMs);
92}
93
94TEST_F(EncoderOvershootDetectorTest, Overshoot) {
95 // Overshoot by 20%.
96 // Allow some error margin due to rounding errors.
97 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
98}
99
100TEST_F(EncoderOvershootDetectorTest, ConstantOvershootVaryingRates) {
101 // Overshoot by 20%, but vary framerate and bitrate.
102 // Allow some error margin due to rounding errors.
103 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
104 target_framerate_fps_ /= 2;
105 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
106 target_bitrate_ = DataRate::bps(target_bitrate_.bps() / 2);
107 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
108}
109
110TEST_F(EncoderOvershootDetectorTest, ConstantRateVaryingOvershoot) {
111 // Overshoot by 10%, keep framerate and bitrate constant.
112 // Allow some error margin due to rounding errors.
113 RunConstantUtilizationTest(1.1, 1.1, 0.01, kWindowSizeMs);
114 // Change overshoot to 20%, run for half window and expect overshoot
115 // to be 15%.
116 RunConstantUtilizationTest(1.2, 1.15, 0.01, kWindowSizeMs / 2);
117 // Keep running at 20% overshoot, after window is full that should now
118 // be the reported overshoot.
119 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
120}
121
122TEST_F(EncoderOvershootDetectorTest, PartialOvershoot) {
123 const int ideal_frame_size_bytes =
124 (target_bitrate_.bps() / target_framerate_fps_) / 8;
125 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
126 rtc::TimeMillis());
127
128 // Test scenario with average bitrate matching the target bitrate, but
129 // with some utilization factor penalty as the frames can't be paced out
130 // on the network at the target rate.
131 // Insert a series of four frames:
132 // 1) 20% overshoot, not penalized as buffer if empty.
133 // 2) 20% overshoot, the 20% overshoot from the first frame is penalized.
134 // 3) 20% undershoot, negating the overshoot from the last frame.
135 // 4) 20% undershoot, no penalty.
136 // On average then utilization penalty is thus 5%.
137
138 int64_t runtime_us = 0;
139 int i = 0;
140 while (runtime_us < kWindowSizeMs * rtc::kNumMicrosecsPerMillisec) {
141 runtime_us += rtc::kNumMicrosecsPerSec / target_framerate_fps_;
142 clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / target_framerate_fps_);
143 int frame_size_bytes = (i++ % 4 < 2) ? (ideal_frame_size_bytes * 120) / 100
144 : (ideal_frame_size_bytes * 80) / 100;
145 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
146 }
147
148 absl::optional<double> utilization_factor =
149 detector_.GetUtilizationFactor(rtc::TimeMillis());
150 EXPECT_NEAR(utilization_factor.value_or(-1), 1.05, 0.01);
151}
152
153} // namespace webrtc