blob: d720d21d7ca65a7aefa2496994e9a4437b5fe907 [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());
Sebastian Jansson40889f32019-04-17 12:11:20 +020042 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +010043 }
44
45 int64_t runtime_us = 0;
46 while (runtime_us < test_duration_ms * 1000) {
47 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
48 runtime_us += rtc::kNumMicrosecsPerSec / target_framerate_fps_;
Sebastian Jansson40889f32019-04-17 12:11:20 +020049 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +010050 }
51
Erik Språng6c072ef2019-04-01 12:57:28 +020052 // At constant utilization, both network and media utilization should be
53 // close to expected.
54 const absl::optional<double> network_utilization_factor =
55 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis());
56 EXPECT_NEAR(network_utilization_factor.value_or(-1),
57 expected_utilization_factor, allowed_error);
58
59 const absl::optional<double> media_utilization_factor =
60 detector_.GetMediaRateUtilizationFactor(rtc::TimeMillis());
61 EXPECT_NEAR(media_utilization_factor.value_or(-1),
62 expected_utilization_factor, allowed_error);
Erik Språng7ca375c2019-02-06 16:20:17 +010063 }
64
65 static constexpr int64_t kWindowSizeMs = 3000;
66 EncoderOvershootDetector detector_;
67 rtc::ScopedFakeClock clock_;
68 DataRate target_bitrate_;
69 double target_framerate_fps_;
70};
71
72TEST_F(EncoderOvershootDetectorTest, NoUtilizationIfNoRate) {
73 const int frame_size_bytes = 1000;
74 const int64_t time_interval_ms = 33;
75 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
76 rtc::TimeMillis());
77
78 // No data points, can't determine overshoot rate.
Erik Språng6c072ef2019-04-01 12:57:28 +020079 EXPECT_FALSE(
80 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
Erik Språng7ca375c2019-02-06 16:20:17 +010081
82 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
Sebastian Jansson40889f32019-04-17 12:11:20 +020083 clock_.AdvanceTime(TimeDelta::ms(time_interval_ms));
Erik Språng6c072ef2019-04-01 12:57:28 +020084 EXPECT_TRUE(
85 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
Erik Språng7ca375c2019-02-06 16:20:17 +010086}
87
88TEST_F(EncoderOvershootDetectorTest, OptimalSize) {
89 // Optimally behaved encoder.
90 // Allow some error margin due to rounding errors, eg due to frame
91 // interval not being an integer.
92 RunConstantUtilizationTest(1.0, 1.0, 0.01, kWindowSizeMs);
93}
94
95TEST_F(EncoderOvershootDetectorTest, Undershoot) {
96 // Undershoot, reported utilization factor should be capped to 1.0 so
97 // that we don't incorrectly boost encoder bitrate during movement.
98 RunConstantUtilizationTest(0.5, 1.0, 0.00, kWindowSizeMs);
99}
100
101TEST_F(EncoderOvershootDetectorTest, Overshoot) {
102 // Overshoot by 20%.
103 // Allow some error margin due to rounding errors.
104 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
105}
106
107TEST_F(EncoderOvershootDetectorTest, ConstantOvershootVaryingRates) {
108 // Overshoot by 20%, but vary framerate and bitrate.
109 // Allow some error margin due to rounding errors.
110 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs);
111 target_framerate_fps_ /= 2;
112 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
113 target_bitrate_ = DataRate::bps(target_bitrate_.bps() / 2);
114 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
115}
116
117TEST_F(EncoderOvershootDetectorTest, ConstantRateVaryingOvershoot) {
118 // Overshoot by 10%, keep framerate and bitrate constant.
119 // Allow some error margin due to rounding errors.
120 RunConstantUtilizationTest(1.1, 1.1, 0.01, kWindowSizeMs);
121 // Change overshoot to 20%, run for half window and expect overshoot
122 // to be 15%.
123 RunConstantUtilizationTest(1.2, 1.15, 0.01, kWindowSizeMs / 2);
124 // Keep running at 20% overshoot, after window is full that should now
125 // be the reported overshoot.
126 RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
127}
128
129TEST_F(EncoderOvershootDetectorTest, PartialOvershoot) {
130 const int ideal_frame_size_bytes =
131 (target_bitrate_.bps() / target_framerate_fps_) / 8;
132 detector_.SetTargetRate(target_bitrate_, target_framerate_fps_,
133 rtc::TimeMillis());
134
135 // Test scenario with average bitrate matching the target bitrate, but
136 // with some utilization factor penalty as the frames can't be paced out
137 // on the network at the target rate.
138 // Insert a series of four frames:
139 // 1) 20% overshoot, not penalized as buffer if empty.
140 // 2) 20% overshoot, the 20% overshoot from the first frame is penalized.
141 // 3) 20% undershoot, negating the overshoot from the last frame.
142 // 4) 20% undershoot, no penalty.
143 // On average then utilization penalty is thus 5%.
144
145 int64_t runtime_us = 0;
146 int i = 0;
147 while (runtime_us < kWindowSizeMs * rtc::kNumMicrosecsPerMillisec) {
148 runtime_us += rtc::kNumMicrosecsPerSec / target_framerate_fps_;
Sebastian Jansson40889f32019-04-17 12:11:20 +0200149 clock_.AdvanceTime(TimeDelta::seconds(1) / target_framerate_fps_);
Erik Språng7ca375c2019-02-06 16:20:17 +0100150 int frame_size_bytes = (i++ % 4 < 2) ? (ideal_frame_size_bytes * 120) / 100
151 : (ideal_frame_size_bytes * 80) / 100;
152 detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
153 }
154
Erik Språng6c072ef2019-04-01 12:57:28 +0200155 // Expect 5% overshoot for network rate, see above.
156 const absl::optional<double> network_utilization_factor =
157 detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis());
158 EXPECT_NEAR(network_utilization_factor.value_or(-1), 1.05, 0.01);
Erik Språng7ca375c2019-02-06 16:20:17 +0100159
Erik Språng6c072ef2019-04-01 12:57:28 +0200160 // Expect media rate to be on average correct.
161 const absl::optional<double> media_utilization_factor =
162 detector_.GetMediaRateUtilizationFactor(rtc::TimeMillis());
163 EXPECT_NEAR(media_utilization_factor.value_or(-1), 1.00, 0.01);
164}
Erik Språng7ca375c2019-02-06 16:20:17 +0100165} // namespace webrtc