xref: /aosp_15_r20/external/webrtc/modules/video_coding/timing/jitter_estimator_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
2  *
3  *  Use of this source code is governed by a BSD-style license
4  *  that can be found in the LICENSE file in the root of the source
5  *  tree. An additional intellectual property rights grant can be found
6  *  in the file PATENTS.  All contributing project authors may
7  *  be found in the AUTHORS file in the root of the source tree.
8  */
9 
10 #include "modules/video_coding/timing/jitter_estimator.h"
11 
12 #include <stdint.h>
13 
14 #include <memory>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 #include "absl/types/optional.h"
20 #include "api/array_view.h"
21 #include "api/field_trials.h"
22 #include "api/units/data_size.h"
23 #include "api/units/frequency.h"
24 #include "api/units/time_delta.h"
25 #include "rtc_base/numerics/histogram_percentile_counter.h"
26 #include "rtc_base/time_utils.h"
27 #include "system_wrappers/include/clock.h"
28 #include "test/gtest.h"
29 
30 namespace webrtc {
31 namespace {
32 
33 // Generates some simple test data in the form of a sawtooth wave.
34 class ValueGenerator {
35  public:
ValueGenerator(int32_t amplitude)36   explicit ValueGenerator(int32_t amplitude)
37       : amplitude_(amplitude), counter_(0) {}
38 
39   virtual ~ValueGenerator() = default;
40 
Delay() const41   TimeDelta Delay() const {
42     return TimeDelta::Millis((counter_ % 11) - 5) * amplitude_;
43   }
44 
FrameSize() const45   DataSize FrameSize() const {
46     return DataSize::Bytes(1000 + Delay().ms() / 5);
47   }
48 
Advance()49   void Advance() { ++counter_; }
50 
51  private:
52   const int32_t amplitude_;
53   int64_t counter_;
54 };
55 
56 class JitterEstimatorTest : public ::testing::Test {
57  protected:
JitterEstimatorTest(const std::string & field_trials)58   explicit JitterEstimatorTest(const std::string& field_trials)
59       : fake_clock_(0),
60         field_trials_(FieldTrials::CreateNoGlobal(field_trials)),
61         estimator_(&fake_clock_, *field_trials_) {}
JitterEstimatorTest()62   JitterEstimatorTest() : JitterEstimatorTest("") {}
~JitterEstimatorTest()63   virtual ~JitterEstimatorTest() {}
64 
Run(int duration_s,int framerate_fps,ValueGenerator & gen)65   void Run(int duration_s, int framerate_fps, ValueGenerator& gen) {
66     TimeDelta tick = 1 / Frequency::Hertz(framerate_fps);
67     for (int i = 0; i < duration_s * framerate_fps; ++i) {
68       estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
69       fake_clock_.AdvanceTime(tick);
70       gen.Advance();
71     }
72   }
73 
74   SimulatedClock fake_clock_;
75   std::unique_ptr<FieldTrials> field_trials_;
76   JitterEstimator estimator_;
77 };
78 
TEST_F(JitterEstimatorTest,SteadyStateConvergence)79 TEST_F(JitterEstimatorTest, SteadyStateConvergence) {
80   ValueGenerator gen(10);
81   Run(/*duration_s=*/60, /*framerate_fps=*/30, gen);
82   EXPECT_EQ(estimator_.GetJitterEstimate(0, absl::nullopt).ms(), 54);
83 }
84 
TEST_F(JitterEstimatorTest,SizeOutlierIsNotRejectedAndIncreasesJitterEstimate)85 TEST_F(JitterEstimatorTest,
86        SizeOutlierIsNotRejectedAndIncreasesJitterEstimate) {
87   ValueGenerator gen(10);
88 
89   // Steady state.
90   Run(/*duration_s=*/60, /*framerate_fps=*/30, gen);
91   TimeDelta steady_state_jitter =
92       estimator_.GetJitterEstimate(0, absl::nullopt);
93 
94   // A single outlier frame size...
95   estimator_.UpdateEstimate(gen.Delay(), 10 * gen.FrameSize());
96   TimeDelta outlier_jitter = estimator_.GetJitterEstimate(0, absl::nullopt);
97 
98   // ...changes the estimate.
99   EXPECT_GT(outlier_jitter.ms(), 1.25 * steady_state_jitter.ms());
100 }
101 
TEST_F(JitterEstimatorTest,LowFramerateDisablesJitterEstimator)102 TEST_F(JitterEstimatorTest, LowFramerateDisablesJitterEstimator) {
103   ValueGenerator gen(10);
104   // At 5 fps, we disable jitter delay altogether.
105   TimeDelta time_delta = 1 / Frequency::Hertz(5);
106   for (int i = 0; i < 60; ++i) {
107     estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
108     fake_clock_.AdvanceTime(time_delta);
109     if (i > 2)
110       EXPECT_EQ(estimator_.GetJitterEstimate(0, absl::nullopt),
111                 TimeDelta::Zero());
112     gen.Advance();
113   }
114 }
115 
TEST_F(JitterEstimatorTest,RttMultAddCap)116 TEST_F(JitterEstimatorTest, RttMultAddCap) {
117   std::vector<std::pair<TimeDelta, rtc::HistogramPercentileCounter>>
118       jitter_by_rtt_mult_cap;
119   jitter_by_rtt_mult_cap.emplace_back(
120       /*rtt_mult_add_cap=*/TimeDelta::Millis(10), /*long_tail_boundary=*/1000);
121   jitter_by_rtt_mult_cap.emplace_back(
122       /*rtt_mult_add_cap=*/TimeDelta::Millis(200), /*long_tail_boundary=*/1000);
123 
124   for (auto& [rtt_mult_add_cap, jitter] : jitter_by_rtt_mult_cap) {
125     estimator_.Reset();
126 
127     ValueGenerator gen(50);
128     TimeDelta time_delta = 1 / Frequency::Hertz(30);
129     constexpr TimeDelta kRtt = TimeDelta::Millis(250);
130     for (int i = 0; i < 100; ++i) {
131       estimator_.UpdateEstimate(gen.Delay(), gen.FrameSize());
132       fake_clock_.AdvanceTime(time_delta);
133       estimator_.FrameNacked();
134       estimator_.UpdateRtt(kRtt);
135       jitter.Add(
136           estimator_.GetJitterEstimate(/*rtt_mult=*/1.0, rtt_mult_add_cap)
137               .ms());
138       gen.Advance();
139     }
140   }
141 
142   // 200ms cap should result in at least 25% higher max compared to 10ms.
143   EXPECT_GT(*jitter_by_rtt_mult_cap[1].second.GetPercentile(1.0),
144             *jitter_by_rtt_mult_cap[0].second.GetPercentile(1.0) * 1.25);
145 }
146 
147 // By default, the `JitterEstimator` is not robust against single large frames.
TEST_F(JitterEstimatorTest,Single2xFrameSizeImpactsJitterEstimate)148 TEST_F(JitterEstimatorTest, Single2xFrameSizeImpactsJitterEstimate) {
149   ValueGenerator gen(10);
150 
151   // Steady state.
152   Run(/*duration_s=*/60, /*framerate_fps=*/30, gen);
153   TimeDelta steady_state_jitter =
154       estimator_.GetJitterEstimate(0, absl::nullopt);
155 
156   // A single outlier frame size...
157   estimator_.UpdateEstimate(gen.Delay(), 2 * gen.FrameSize());
158   TimeDelta outlier_jitter = estimator_.GetJitterEstimate(0, absl::nullopt);
159 
160   // ...impacts the estimate.
161   EXPECT_GT(outlier_jitter.ms(), steady_state_jitter.ms());
162 }
163 
164 // Under the default config, congested frames are used when calculating the
165 // noise variance, meaning that they will impact the final jitter estimate.
TEST_F(JitterEstimatorTest,CongestedFrameImpactsJitterEstimate)166 TEST_F(JitterEstimatorTest, CongestedFrameImpactsJitterEstimate) {
167   ValueGenerator gen(10);
168 
169   // Steady state.
170   Run(/*duration_s=*/10, /*framerate_fps=*/30, gen);
171   TimeDelta steady_state_jitter =
172       estimator_.GetJitterEstimate(0, absl::nullopt);
173 
174   // Congested frame...
175   estimator_.UpdateEstimate(-10 * gen.Delay(), 0.1 * gen.FrameSize());
176   TimeDelta outlier_jitter = estimator_.GetJitterEstimate(0, absl::nullopt);
177 
178   // ...impacts the estimate.
179   EXPECT_GT(outlier_jitter.ms(), steady_state_jitter.ms());
180 }
181 
TEST_F(JitterEstimatorTest,EmptyFieldTrialsParsesToUnsetConfig)182 TEST_F(JitterEstimatorTest, EmptyFieldTrialsParsesToUnsetConfig) {
183   JitterEstimator::Config config = estimator_.GetConfigForTest();
184   EXPECT_FALSE(config.avg_frame_size_median);
185   EXPECT_FALSE(config.max_frame_size_percentile.has_value());
186   EXPECT_FALSE(config.frame_size_window.has_value());
187   EXPECT_FALSE(config.num_stddev_delay_clamp.has_value());
188   EXPECT_FALSE(config.num_stddev_delay_outlier.has_value());
189   EXPECT_FALSE(config.num_stddev_size_outlier.has_value());
190   EXPECT_FALSE(config.congestion_rejection_factor.has_value());
191   EXPECT_TRUE(config.estimate_noise_when_congested);
192 }
193 
194 class FieldTrialsOverriddenJitterEstimatorTest : public JitterEstimatorTest {
195  protected:
FieldTrialsOverriddenJitterEstimatorTest()196   FieldTrialsOverriddenJitterEstimatorTest()
197       : JitterEstimatorTest(
198             "WebRTC-JitterEstimatorConfig/"
199             "avg_frame_size_median:true,"
200             "max_frame_size_percentile:0.9,"
201             "frame_size_window:30,"
202             "num_stddev_delay_clamp:1.1,"
203             "num_stddev_delay_outlier:2,"
204             "num_stddev_size_outlier:3.1,"
205             "congestion_rejection_factor:-1.55,"
206             "estimate_noise_when_congested:false/") {}
~FieldTrialsOverriddenJitterEstimatorTest()207   ~FieldTrialsOverriddenJitterEstimatorTest() {}
208 };
209 
TEST_F(FieldTrialsOverriddenJitterEstimatorTest,FieldTrialsParsesCorrectly)210 TEST_F(FieldTrialsOverriddenJitterEstimatorTest, FieldTrialsParsesCorrectly) {
211   JitterEstimator::Config config = estimator_.GetConfigForTest();
212   EXPECT_TRUE(config.avg_frame_size_median);
213   EXPECT_EQ(*config.max_frame_size_percentile, 0.9);
214   EXPECT_EQ(*config.frame_size_window, 30);
215   EXPECT_EQ(*config.num_stddev_delay_clamp, 1.1);
216   EXPECT_EQ(*config.num_stddev_delay_outlier, 2.0);
217   EXPECT_EQ(*config.num_stddev_size_outlier, 3.1);
218   EXPECT_EQ(*config.congestion_rejection_factor, -1.55);
219   EXPECT_FALSE(config.estimate_noise_when_congested);
220 }
221 
TEST_F(FieldTrialsOverriddenJitterEstimatorTest,DelayOutlierIsRejectedAndMaintainsJitterEstimate)222 TEST_F(FieldTrialsOverriddenJitterEstimatorTest,
223        DelayOutlierIsRejectedAndMaintainsJitterEstimate) {
224   ValueGenerator gen(10);
225 
226   // Steady state.
227   Run(/*duration_s=*/60, /*framerate_fps=*/30, gen);
228   TimeDelta steady_state_jitter =
229       estimator_.GetJitterEstimate(0, absl::nullopt);
230 
231   // A single outlier frame size...
232   estimator_.UpdateEstimate(10 * gen.Delay(), gen.FrameSize());
233   TimeDelta outlier_jitter = estimator_.GetJitterEstimate(0, absl::nullopt);
234 
235   // ...does not change the estimate.
236   EXPECT_EQ(outlier_jitter.ms(), steady_state_jitter.ms());
237 }
238 
239 // The field trial is configured to be robust against the `(1 - 0.9) = 10%`
240 // largest frames over a window of length `30`.
TEST_F(FieldTrialsOverriddenJitterEstimatorTest,Four2xFrameSizesImpactJitterEstimate)241 TEST_F(FieldTrialsOverriddenJitterEstimatorTest,
242        Four2xFrameSizesImpactJitterEstimate) {
243   ValueGenerator gen(10);
244 
245   // Steady state.
246   Run(/*duration_s=*/60, /*framerate_fps=*/30, gen);
247   TimeDelta steady_state_jitter =
248       estimator_.GetJitterEstimate(0, absl::nullopt);
249 
250   // Three outlier frames do not impact the jitter estimate.
251   for (int i = 0; i < 3; ++i) {
252     estimator_.UpdateEstimate(gen.Delay(), 2 * gen.FrameSize());
253   }
254   TimeDelta outlier_jitter_3x = estimator_.GetJitterEstimate(0, absl::nullopt);
255   EXPECT_EQ(outlier_jitter_3x.ms(), steady_state_jitter.ms());
256 
257   // Four outlier frames do impact the jitter estimate.
258   estimator_.UpdateEstimate(gen.Delay(), 2 * gen.FrameSize());
259   TimeDelta outlier_jitter_4x = estimator_.GetJitterEstimate(0, absl::nullopt);
260   EXPECT_GT(outlier_jitter_4x.ms(), outlier_jitter_3x.ms());
261 }
262 
263 // When so configured, congested frames are NOT used when calculating the
264 // noise variance, meaning that they will NOT impact the final jitter estimate.
TEST_F(FieldTrialsOverriddenJitterEstimatorTest,CongestedFrameDoesNotImpactJitterEstimate)265 TEST_F(FieldTrialsOverriddenJitterEstimatorTest,
266        CongestedFrameDoesNotImpactJitterEstimate) {
267   ValueGenerator gen(10);
268 
269   // Steady state.
270   Run(/*duration_s=*/10, /*framerate_fps=*/30, gen);
271   TimeDelta steady_state_jitter =
272       estimator_.GetJitterEstimate(0, absl::nullopt);
273 
274   // Congested frame...
275   estimator_.UpdateEstimate(-10 * gen.Delay(), 0.1 * gen.FrameSize());
276   TimeDelta outlier_jitter = estimator_.GetJitterEstimate(0, absl::nullopt);
277 
278   // ...does not impact the estimate.
279   EXPECT_EQ(outlier_jitter.ms(), steady_state_jitter.ms());
280 }
281 
282 class MisconfiguredFieldTrialsJitterEstimatorTest : public JitterEstimatorTest {
283  protected:
MisconfiguredFieldTrialsJitterEstimatorTest()284   MisconfiguredFieldTrialsJitterEstimatorTest()
285       : JitterEstimatorTest(
286             "WebRTC-JitterEstimatorConfig/"
287             "max_frame_size_percentile:-0.9,"
288             "frame_size_window:-1,"
289             "num_stddev_delay_clamp:-1.9,"
290             "num_stddev_delay_outlier:-2,"
291             "num_stddev_size_outlier:-23.1/") {}
~MisconfiguredFieldTrialsJitterEstimatorTest()292   ~MisconfiguredFieldTrialsJitterEstimatorTest() {}
293 };
294 
TEST_F(MisconfiguredFieldTrialsJitterEstimatorTest,FieldTrialsAreValidated)295 TEST_F(MisconfiguredFieldTrialsJitterEstimatorTest, FieldTrialsAreValidated) {
296   JitterEstimator::Config config = estimator_.GetConfigForTest();
297   EXPECT_EQ(*config.max_frame_size_percentile, 0.0);
298   EXPECT_EQ(*config.frame_size_window, 1);
299   EXPECT_EQ(*config.num_stddev_delay_clamp, 0.0);
300   EXPECT_EQ(*config.num_stddev_delay_outlier, 0.0);
301   EXPECT_EQ(*config.num_stddev_size_outlier, 0.0);
302 }
303 
304 }  // namespace
305 }  // namespace webrtc
306