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