1 /*
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 
13 #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 
18 namespace webrtc {
19 
20 class EncoderOvershootDetectorTest : public ::testing::Test {
21  public:
22   static constexpr int kDefaultBitrateBps = 300000;
23   static constexpr double kDefaultFrameRateFps = 15;
EncoderOvershootDetectorTest()24   EncoderOvershootDetectorTest()
25       : detector_(kWindowSizeMs),
26         target_bitrate_(DataRate::BitsPerSec(kDefaultBitrateBps)),
27         target_framerate_fps_(kDefaultFrameRateFps) {}
28 
29  protected:
RunConstantUtilizationTest(double actual_utilization_factor,double expected_utilization_factor,double allowed_error,int64_t test_duration_ms)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());
43       clock_.AdvanceTime(TimeDelta::Seconds(1) / 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_.AdvanceTime(TimeDelta::Seconds(1) / target_framerate_fps_);
51     }
52 
53     // 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);
64   }
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 
TEST_F(EncoderOvershootDetectorTest,NoUtilizationIfNoRate)73 TEST_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.
80   EXPECT_FALSE(
81       detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
82 
83   detector_.OnEncodedFrame(frame_size_bytes, rtc::TimeMillis());
84   clock_.AdvanceTime(TimeDelta::Millis(time_interval_ms));
85   EXPECT_TRUE(
86       detector_.GetNetworkRateUtilizationFactor(rtc::TimeMillis()).has_value());
87 }
88 
TEST_F(EncoderOvershootDetectorTest,OptimalSize)89 TEST_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 
TEST_F(EncoderOvershootDetectorTest,Undershoot)96 TEST_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 
TEST_F(EncoderOvershootDetectorTest,Overshoot)102 TEST_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 
TEST_F(EncoderOvershootDetectorTest,ConstantOvershootVaryingRates)108 TEST_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::BitsPerSec(target_bitrate_.bps() / 2);
115   RunConstantUtilizationTest(1.2, 1.2, 0.01, kWindowSizeMs / 2);
116 }
117 
TEST_F(EncoderOvershootDetectorTest,ConstantRateVaryingOvershoot)118 TEST_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 
TEST_F(EncoderOvershootDetectorTest,PartialOvershoot)130 TEST_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_;
150     clock_.AdvanceTime(TimeDelta::Seconds(1) / target_framerate_fps_);
151     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 
156   // 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);
160 
161   // 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 }
166 }  // namespace webrtc
167