blob: 7170f490615ef93ee063bf42c60ef667133ffdaa [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"
Jonas Olssona4d87372019-07-05 19:08:33 +020012
Erik Språng7ca375c2019-02-06 16:20:17 +010013#include "api/units/data_rate.h"
14#include "rtc_base/fake_clock.h"
15#include "rtc_base/time_utils.h"
16#include "test/gtest.h"
17
18namespace webrtc {
19
20class EncoderOvershootDetectorTest : public ::testing::Test {
21 public:
22 static constexpr int kDefaultBitrateBps = 300000;
23 static constexpr double kDefaultFrameRateFps = 15;
24 EncoderOvershootDetectorTest()
25 : detector_(kWindowSizeMs),
26 target_bitrate_(DataRate::bps(kDefaultBitrateBps)),
27 target_framerate_fps_(kDefaultFrameRateFps) {}
28
29 protected:
30 void RunConstantUtilizationTest(double actual_utilization_factor,
31 double expected_utilization_factor,
32 double allowed_error,
33 int64_t test_duration_ms) {
34 const int frame_size_bytes =
35 static_cast<int>(actual_utilization_factor *
36 (target_bitrate_.bps() / target_framerate_fps_) / 8);
37 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
38 rtc::TimeMillis());
39
40 if (rtc::TimeMillis() == 0) {
41 // Encode a first frame which by definition has no overuse factor.
42 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
Sebastian Jansson40889f32019-04-17 12:11:20 +020043 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +010044 }
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_;
Sebastian Jansson40889f32019-04-17 12:11:20 +020050 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +010051 }
52
Erik Språng6c072ef2019-04-01 12:57:28 +020053 // At constant utilization, both network and media utilization should be
54 // close to expected.
55 const absl::optional<double> network_utilization_factor =
56 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis());
57 EXPECT_NEAR(network_utilization_factor.value_or(-1),
58 expected_utilization_factor, allowed_error);
59
60 const absl::optional<double> media_utilization_factor =
61 detector_.GetMediaRateUtilizationFactor(rtc::TimeMillis());
62 EXPECT_NEAR(media_utilization_factor.value_or(-1),
63 expected_utilization_factor, allowed_error);
Erik Språng7ca375c2019-02-06 16:20:17 +010064 }
65
66 static constexpr int64_t kWindowSizeMs = 3000;
67 EncoderOvershootDetector detector_;
68 rtc::ScopedFakeClock clock_;
69 DataRate target_bitrate_;
70 double target_framerate_fps_;
71};
72
73TEST_F(EncoderOvershootDetectorTest, NoUtilizationIfNoRate) {
74 const int frame_size_bytes = 1000;
75 const int64_t time_interval_ms = 33;
76 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
77 rtc::TimeMillis());
78
79 // No data points, can't determine overshoot rate.
Erik Språng6c072ef2019-04-01 12:57:28 +020080 EXPECT_FALSE(
81 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
Erik Språng7ca375c2019-02-06 16:20:17 +010082
83 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
Sebastian Jansson40889f32019-04-17 12:11:20 +020084 clock_.AdvanceTime(TimeDelta::ms(time_interval_ms));
Erik Språng6c072ef2019-04-01 12:57:28 +020085 EXPECT_TRUE(
86 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
Erik Språng7ca375c2019-02-06 16:20:17 +010087}
88
89TEST_F(EncoderOvershootDetectorTest, OptimalSize) {
90 // Optimally behaved encoder.
91 // Allow some error margin due to rounding errors, eg due to frame
92 // interval not being an integer.
93 RunConstantUtilizationTest(1.0, 1.0, 0.01, kWindowSizeMs);
94}
95
96TEST_F(EncoderOvershootDetectorTest, Undershoot) {
97 // Undershoot, reported utilization factor should be capped to 1.0 so
98 // that we don't incorrectly boost encoder bitrate during movement.
99 RunConstantUtilizationTest(0.5, 1.0, 0.00, kWindowSizeMs);
100}
101
102TEST_F(EncoderOvershootDetectorTest, Overshoot) {
103 // Overshoot by 20%.
104 // Allow some error margin due to rounding errors.
105 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
106}
107
108TEST_F(EncoderOvershootDetectorTest, ConstantOvershootVaryingRates) {
109 // Overshoot by 20%, but vary framerate and bitrate.
110 // Allow some error margin due to rounding errors.
111 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
112 target_framerate_fps_ /= 2;
113 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
114 target_bitrate_ = DataRate::bps(target_bitrate_.bps() / 2);
115 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
116}
117
118TEST_F(EncoderOvershootDetectorTest, ConstantRateVaryingOvershoot) {
119 // Overshoot by 10%, keep framerate and bitrate constant.
120 // Allow some error margin due to rounding errors.
121 RunConstantUtilizationTest(1.1, 1.1, 0.01, kWindowSizeMs);
122 // Change overshoot to 20%, run for half window and expect overshoot
123 // to be 15%.
124 RunConstantUtilizationTest(1.2, 1.15, 0.01, kWindowSizeMs / 2);
125 // Keep running at 20% overshoot, after window is full that should now
126 // be the reported overshoot.
127 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
128}
129
130TEST_F(EncoderOvershootDetectorTest, PartialOvershoot) {
131 const int ideal_frame_size_bytes =
132 (target_bitrate_.bps() / target_framerate_fps_) / 8;
133 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
134 rtc::TimeMillis());
135
136 // Test scenario with average bitrate matching the target bitrate, but
137 // with some utilization factor penalty as the frames can't be paced out
138 // on the network at the target rate.
139 // Insert a series of four frames:
140 // 1) 20% overshoot, not penalized as buffer if empty.
141 // 2) 20% overshoot, the 20% overshoot from the first frame is penalized.
142 // 3) 20% undershoot, negating the overshoot from the last frame.
143 // 4) 20% undershoot, no penalty.
144 // On average then utilization penalty is thus 5%.
145
146 int64_t runtime_us = 0;
147 int i = 0;
148 while (runtime_us < kWindowSizeMs * rtc::kNumMicrosecsPerMillisec) {
149 runtime_us += rtc::kNumMicrosecsPerSec / target_framerate_fps_;
Sebastian Jansson40889f32019-04-17 12:11:20 +0200150 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +0100151 int frame_size_bytes = (i++ % 4 < 2) ? (ideal_frame_size_bytes * 120) / 100
152 : (ideal_frame_size_bytes * 80) / 100;
153 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
154 }
155
Erik Språng6c072ef2019-04-01 12:57:28 +0200156 // Expect 5% overshoot for network rate, see above.
157 const absl::optional<double> network_utilization_factor =
158 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis());
159 EXPECT_NEAR(network_utilization_factor.value_or(-1), 1.05, 0.01);
Erik Språng7ca375c2019-02-06 16:20:17 +0100160
Erik Språng6c072ef2019-04-01 12:57:28 +0200161 // Expect media rate to be on average correct.
162 const absl::optional<double> media_utilization_factor =
163 detector_.GetMediaRateUtilizationFactor(rtc::TimeMillis());
164 EXPECT_NEAR(media_utilization_factor.value_or(-1), 1.00, 0.01);
165}
Erik Språng7ca375c2019-02-06 16:20:17 +0100166} // namespace webrtc