/* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include #include #include "absl/types/optional.h" #include "api/array_view.h" #include "modules/video_coding/jitter_estimator.h" #include "rtc_base/experiments/jitter_upper_bound_experiment.h" #include "rtc_base/numerics/histogram_percentile_counter.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/time_utils.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" namespace webrtc { class TestVCMJitterEstimator : public ::testing::Test { protected: TestVCMJitterEstimator() : fake_clock_(0) {} virtual void SetUp() { estimator_ = std::make_unique(&fake_clock_); } void AdvanceClock(int64_t microseconds) { fake_clock_.AdvanceTimeMicroseconds(microseconds); } SimulatedClock fake_clock_; std::unique_ptr estimator_; }; // Generates some simple test data in the form of a sawtooth wave. class ValueGenerator { public: explicit ValueGenerator(int32_t amplitude) : amplitude_(amplitude), counter_(0) {} virtual ~ValueGenerator() {} int64_t Delay() const { return ((counter_ % 11) - 5) * amplitude_; } uint32_t FrameSize() const { return 1000 + Delay(); } void Advance() { ++counter_; } private: const int32_t amplitude_; int64_t counter_; }; // 5 fps, disable jitter delay altogether. TEST_F(TestVCMJitterEstimator, TestLowRate) { ValueGenerator gen(10); uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); AdvanceClock(time_delta_us); if (i > 2) EXPECT_EQ(estimator_->GetJitterEstimate(0, absl::nullopt), 0); gen.Advance(); } } TEST_F(TestVCMJitterEstimator, TestLowRateDisabled) { test::ScopedFieldTrials field_trials( "WebRTC-ReducedJitterDelayKillSwitch/Enabled/"); SetUp(); ValueGenerator gen(10); uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); AdvanceClock(time_delta_us); if (i > 2) EXPECT_GT(estimator_->GetJitterEstimate(0, absl::nullopt), 0); gen.Advance(); } } TEST_F(TestVCMJitterEstimator, TestUpperBound) { struct TestContext { TestContext() : upper_bound(0.0), rtt_mult(0), rtt_mult_add_cap_ms(absl::nullopt), percentiles(1000) {} double upper_bound; double rtt_mult; absl::optional rtt_mult_add_cap_ms; rtc::HistogramPercentileCounter percentiles; }; std::vector test_cases(4); // Large upper bound, rtt_mult = 0, and nullopt for rtt_mult addition cap. test_cases[0].upper_bound = 100.0; test_cases[0].rtt_mult = 0; test_cases[0].rtt_mult_add_cap_ms = absl::nullopt; // Small upper bound, rtt_mult = 0, and nullopt for rtt_mult addition cap. test_cases[1].upper_bound = 3.5; test_cases[1].rtt_mult = 0; test_cases[1].rtt_mult_add_cap_ms = absl::nullopt; // Large upper bound, rtt_mult = 1, and large rtt_mult addition cap value. test_cases[2].upper_bound = 1000.0; test_cases[2].rtt_mult = 1.0; test_cases[2].rtt_mult_add_cap_ms = 200.0; // Large upper bound, rtt_mult = 1, and small rtt_mult addition cap value. test_cases[3].upper_bound = 1000.0; test_cases[3].rtt_mult = 1.0; test_cases[3].rtt_mult_add_cap_ms = 10.0; // Test jitter buffer upper_bound and rtt_mult addition cap sizes. for (TestContext& context : test_cases) { // Set up field trial and reset jitter estimator. char string_buf[64]; rtc::SimpleStringBuilder ssb(string_buf); ssb << JitterUpperBoundExperiment::kJitterUpperBoundExperimentName << "/Enabled-" << context.upper_bound << "/"; test::ScopedFieldTrials field_trials(ssb.str()); SetUp(); ValueGenerator gen(50); uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 30; constexpr int64_t kRttMs = 250; for (int i = 0; i < 100; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); AdvanceClock(time_delta_us); estimator_->FrameNacked(); // To test rtt_mult. estimator_->UpdateRtt(kRttMs); // To test rtt_mult. context.percentiles.Add( static_cast(estimator_->GetJitterEstimate( context.rtt_mult, context.rtt_mult_add_cap_ms))); gen.Advance(); } } // Median should be similar after three seconds. Allow 5% error margin. uint32_t median_unbound = *test_cases[0].percentiles.GetPercentile(0.5); uint32_t median_bounded = *test_cases[1].percentiles.GetPercentile(0.5); EXPECT_NEAR(median_unbound, median_bounded, (median_unbound * 5) / 100); // Max should be lower for the bounded case. uint32_t max_unbound = *test_cases[0].percentiles.GetPercentile(1.0); uint32_t max_bounded = *test_cases[1].percentiles.GetPercentile(1.0); EXPECT_GT(max_unbound, static_cast(max_bounded * 1.25)); // With rtt_mult = 1, max should be lower with small rtt_mult add cap value. max_unbound = *test_cases[2].percentiles.GetPercentile(1.0); max_bounded = *test_cases[3].percentiles.GetPercentile(1.0); EXPECT_GT(max_unbound, static_cast(max_bounded * 1.25)); } } // namespace webrtc