1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/video_cadence_estimator.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/test/scoped_feature_list.h"
17 #include "base/time/time.h"
18 #include "media/base/media_switches.h"
19 #include "testing/gtest/include/gtest/gtest.h"
23 // See VideoCadenceEstimator header for more details.
24 constexpr auto kMinimumAcceptableTimeBetweenGlitches = base::Seconds(8);
26 // Slows down the given |fps| according to NTSC field reduction standards; see
27 // http://en.wikipedia.org/wiki/Frame_rate#Digital_video_and_television
28 static double NTSC(double fps) {
32 static base::TimeDelta Interval(double hertz) {
33 return base::Seconds(1.0 / hertz);
36 std::vector<int> CreateCadenceFromString(const std::string& cadence) {
37 CHECK_EQ('[', cadence.front());
38 CHECK_EQ(']', cadence.back());
40 std::vector<int> result;
41 for (const std::string& token :
42 base::SplitString(cadence.substr(1, cadence.length() - 2),
43 ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
44 int cadence_value = 0;
45 CHECK(base::StringToInt(token, &cadence_value)) << token;
46 result.push_back(cadence_value);
52 static void VerifyCadenceVectorWithCustomDeviationAndDrift(
53 VideoCadenceEstimator* estimator,
56 base::TimeDelta deviation,
57 base::TimeDelta acceptable_drift,
58 const std::string& expected_cadence) {
59 SCOPED_TRACE(base::StringPrintf("Checking %.03f fps into %0.03f", frame_hertz,
62 const std::vector<int> expected_cadence_vector =
63 CreateCadenceFromString(expected_cadence);
66 const bool cadence_changed = estimator->UpdateCadenceEstimate(
67 Interval(render_hertz), Interval(frame_hertz), deviation,
69 EXPECT_EQ(cadence_changed, estimator->has_cadence());
70 EXPECT_EQ(expected_cadence_vector.empty(), !estimator->has_cadence());
72 // Nothing further to test.
73 if (expected_cadence_vector.empty() || !estimator->has_cadence())
76 EXPECT_EQ(expected_cadence_vector.size(),
77 estimator->cadence_size_for_testing());
79 // Spot two cycles of the cadence.
80 for (size_t i = 0; i < expected_cadence_vector.size() * 2; ++i) {
81 ASSERT_EQ(expected_cadence_vector[i % expected_cadence_vector.size()],
82 estimator->GetCadenceForFrame(i));
86 static void VerifyCadenceVectorWithCustomDrift(
87 VideoCadenceEstimator* estimator,
90 base::TimeDelta acceptable_drift,
91 const std::string& expected_cadence) {
92 VerifyCadenceVectorWithCustomDeviationAndDrift(
93 estimator, frame_hertz, render_hertz, base::TimeDelta(), acceptable_drift,
97 static void VerifyCadenceVectorWithCustomDeviation(
98 VideoCadenceEstimator* estimator,
101 base::TimeDelta deviation,
102 const std::string& expected_cadence) {
103 const base::TimeDelta acceptable_drift =
104 std::max(Interval(frame_hertz) / 2, Interval(render_hertz));
105 VerifyCadenceVectorWithCustomDeviationAndDrift(
106 estimator, frame_hertz, render_hertz, deviation, acceptable_drift,
110 static void VerifyCadenceVector(VideoCadenceEstimator* estimator,
113 const std::string& expected_cadence) {
114 const base::TimeDelta acceptable_drift =
115 std::max(Interval(frame_hertz) / 2, Interval(render_hertz));
116 VerifyCadenceVectorWithCustomDeviationAndDrift(
117 estimator, frame_hertz, render_hertz, base::TimeDelta(), acceptable_drift,
121 // Spot check common display and frame rate pairs for correctness.
122 TEST(VideoCadenceEstimatorTest, CadenceCalculations) {
123 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
124 estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
126 const std::string kEmptyCadence = "[]";
127 VerifyCadenceVector(&estimator, 1, NTSC(60), "[60]");
129 VerifyCadenceVector(&estimator, 24, 60, "[3:2]");
130 VerifyCadenceVector(&estimator, NTSC(24), 60, "[3:2]");
131 VerifyCadenceVector(&estimator, 24, NTSC(60), "[3:2]");
133 VerifyCadenceVector(&estimator, 25, 60, "[2:3:2:3:2]");
134 VerifyCadenceVector(&estimator, NTSC(25), 60, "[2:3:2:3:2]");
135 VerifyCadenceVector(&estimator, 25, NTSC(60), "[2:3:2:3:2]");
137 VerifyCadenceVector(&estimator, 30, 60, "[2]");
138 VerifyCadenceVector(&estimator, NTSC(30), 60, "[2]");
139 VerifyCadenceVector(&estimator, 29.5, 60, kEmptyCadence);
141 VerifyCadenceVector(&estimator, 50, 60, "[1:1:2:1:1]");
142 VerifyCadenceVector(&estimator, NTSC(50), 60, "[1:1:2:1:1]");
143 VerifyCadenceVector(&estimator, 50, NTSC(60), "[1:1:2:1:1]");
145 VerifyCadenceVector(&estimator, NTSC(60), 60, "[1]");
146 VerifyCadenceVector(&estimator, 60, NTSC(60), "[1]");
148 VerifyCadenceVector(&estimator, 120, 60, "[1:0]");
149 VerifyCadenceVector(&estimator, NTSC(120), 60, "[1:0]");
150 VerifyCadenceVector(&estimator, 120, NTSC(60), "[1:0]");
152 // Test cases for cadence below 1.
153 VerifyCadenceVector(&estimator, 120, 24, "[1:0:0:0:0]");
154 VerifyCadenceVector(&estimator, 120, 48, "[1:0:0:1:0]");
155 VerifyCadenceVector(&estimator, 120, 72, "[1:0:1:0:1]");
156 VerifyCadenceVector(&estimator, 90, 60, "[1:0:1]");
158 // 50Hz is common in the EU.
159 VerifyCadenceVector(&estimator, NTSC(24), 50, kEmptyCadence);
160 VerifyCadenceVector(&estimator, 24, 50, kEmptyCadence);
162 VerifyCadenceVector(&estimator, NTSC(25), 50, "[2]");
163 VerifyCadenceVector(&estimator, 25, 50, "[2]");
165 VerifyCadenceVector(&estimator, NTSC(30), 50, "[2:1:2]");
166 VerifyCadenceVector(&estimator, 30, 50, "[2:1:2]");
168 VerifyCadenceVector(&estimator, NTSC(60), 50, kEmptyCadence);
169 VerifyCadenceVector(&estimator, 60, 50, kEmptyCadence);
173 // Check the extreme case that max_acceptable_drift is larger than
174 // minimum_time_until_max_drift.
175 TEST(VideoCadenceEstimatorTest, CadenceCalculationWithLargeDrift) {
176 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
177 estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
179 base::TimeDelta drift = base::Hours(1);
180 VerifyCadenceVectorWithCustomDrift(&estimator, 1, NTSC(60), drift, "[60]");
182 VerifyCadenceVectorWithCustomDrift(&estimator, 30, 60, drift, "[2]");
183 VerifyCadenceVectorWithCustomDrift(&estimator, NTSC(30), 60, drift, "[2]");
184 VerifyCadenceVectorWithCustomDrift(&estimator, 30, NTSC(60), drift, "[2]");
186 VerifyCadenceVectorWithCustomDrift(&estimator, 25, 60, drift, "[2]");
187 VerifyCadenceVectorWithCustomDrift(&estimator, NTSC(25), 60, drift, "[2]");
188 VerifyCadenceVectorWithCustomDrift(&estimator, 25, NTSC(60), drift, "[2]");
190 // Test cases for cadence below 1.
191 VerifyCadenceVectorWithCustomDrift(&estimator, 120, 24, drift, "[1]");
192 VerifyCadenceVectorWithCustomDrift(&estimator, 120, 48, drift, "[1]");
193 VerifyCadenceVectorWithCustomDrift(&estimator, 120, 72, drift, "[1]");
194 VerifyCadenceVectorWithCustomDrift(&estimator, 90, 60, drift, "[1]");
197 TEST(VideoCadenceEstimatorTest, SimpleCadenceTest) {
198 bool simple_cadence = VideoCadenceEstimator::HasSimpleCadence(
199 Interval(60), Interval(30), kMinimumAcceptableTimeBetweenGlitches);
200 // 60 Hz screen with 30 FPS video should be considered a simple cadence.
201 EXPECT_TRUE(simple_cadence);
202 simple_cadence = VideoCadenceEstimator::HasSimpleCadence(
203 Interval(60), Interval(24), kMinimumAcceptableTimeBetweenGlitches);
204 EXPECT_FALSE(simple_cadence);
207 // Check the case that the estimator excludes variable FPS case from Cadence.
208 TEST(VideoCadenceEstimatorTest, CadenceCalculationWithLargeDeviation) {
209 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
210 estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
212 const base::TimeDelta deviation = base::Milliseconds(30);
213 VerifyCadenceVectorWithCustomDeviation(&estimator, 1, 60, deviation, "[]");
214 VerifyCadenceVectorWithCustomDeviation(&estimator, 30, 60, deviation, "[]");
215 VerifyCadenceVectorWithCustomDeviation(&estimator, 25, 60, deviation, "[]");
217 // Test cases for cadence with low refresh rate.
218 VerifyCadenceVectorWithCustomDeviation(&estimator, 60, 12, deviation,
222 TEST(VideoCadenceEstimatorTest, CadenceVariesWithAcceptableDrift) {
223 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
224 estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
226 const base::TimeDelta render_interval = Interval(NTSC(60));
227 const base::TimeDelta frame_interval = Interval(120);
229 base::TimeDelta acceptable_drift = frame_interval / 2;
230 EXPECT_FALSE(estimator.UpdateCadenceEstimate(
231 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
232 EXPECT_FALSE(estimator.has_cadence());
234 // Increasing the acceptable drift should be result in more permissive
235 // detection of cadence.
236 acceptable_drift = render_interval;
237 EXPECT_TRUE(estimator.UpdateCadenceEstimate(
238 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
239 EXPECT_TRUE(estimator.has_cadence());
240 EXPECT_EQ("[1:0]", estimator.GetCadenceForTesting());
243 TEST(VideoCadenceEstimatorTest, CadenceVariesWithAcceptableGlitchTime) {
244 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
245 estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
247 const base::TimeDelta render_interval = Interval(NTSC(60));
248 const base::TimeDelta frame_interval = Interval(120);
249 const base::TimeDelta acceptable_drift = frame_interval / 2;
251 EXPECT_FALSE(estimator.UpdateCadenceEstimate(
252 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
253 EXPECT_FALSE(estimator.has_cadence());
255 // Decreasing the acceptable glitch time should be result in more permissive
256 // detection of cadence.
257 VideoCadenceEstimator permissive_estimator(
258 kMinimumAcceptableTimeBetweenGlitches / 2);
259 permissive_estimator.set_cadence_hysteresis_threshold_for_testing(
261 EXPECT_TRUE(permissive_estimator.UpdateCadenceEstimate(
262 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
263 EXPECT_TRUE(permissive_estimator.has_cadence());
264 EXPECT_EQ("[1:0]", permissive_estimator.GetCadenceForTesting());
267 TEST(VideoCadenceEstimatorTest, CadenceHystersisPreventsOscillation) {
268 VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
270 const base::TimeDelta render_interval = Interval(30);
271 const base::TimeDelta frame_interval = Interval(60);
272 const base::TimeDelta acceptable_drift = frame_interval / 2;
273 estimator.set_cadence_hysteresis_threshold_for_testing(render_interval * 2);
275 // Cadence hysteresis should prevent the cadence from taking effect yet.
276 EXPECT_FALSE(estimator.UpdateCadenceEstimate(
277 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
278 EXPECT_FALSE(estimator.has_cadence());
280 // A second call should exceed cadence hysteresis and take into effect.
281 EXPECT_TRUE(estimator.UpdateCadenceEstimate(
282 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
283 EXPECT_TRUE(estimator.has_cadence());
285 // One bad interval shouldn't cause cadence to drop
287 estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
288 base::TimeDelta(), acceptable_drift));
289 EXPECT_TRUE(estimator.has_cadence());
291 // Resumption of cadence should clear bad interval count.
292 EXPECT_FALSE(estimator.UpdateCadenceEstimate(
293 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
294 EXPECT_TRUE(estimator.has_cadence());
296 // So one more bad interval shouldn't cause cadence to drop
298 estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
299 base::TimeDelta(), acceptable_drift));
300 EXPECT_TRUE(estimator.has_cadence());
302 // Two bad intervals should.
304 estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
305 base::TimeDelta(), acceptable_drift));
306 EXPECT_FALSE(estimator.has_cadence());
309 void VerifyCadenceSequence(VideoCadenceEstimator* estimator,
312 std::vector<int> expected_cadence) {
313 SCOPED_TRACE(base::StringPrintf("Checking %.03f fps into %0.03f", frame_rate,
316 const base::TimeDelta render_interval = Interval(display_rate);
317 const base::TimeDelta frame_interval = Interval(frame_rate);
318 const base::TimeDelta acceptable_drift =
319 frame_interval < render_interval ? render_interval : frame_interval;
320 const base::TimeDelta test_runtime = base::Seconds(10 * 60);
321 const int test_frames = base::ClampFloor(test_runtime / frame_interval);
324 EXPECT_TRUE(estimator->UpdateCadenceEstimate(
325 render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
326 EXPECT_TRUE(estimator->has_cadence());
327 for (auto i = 0u; i < expected_cadence.size(); i++) {
328 ASSERT_EQ(expected_cadence[i], estimator->GetCadenceForFrame(i))
332 int total_display_cycles = 0;
333 for (int i = 0; i < test_frames; i++) {
334 total_display_cycles += estimator->GetCadenceForFrame(i);
335 base::TimeDelta drift =
336 (total_display_cycles * render_interval) - ((i + 1) * frame_interval);
337 EXPECT_LE(drift.magnitude(), acceptable_drift)
338 << " i=" << i << " time=" << (total_display_cycles * render_interval);
339 if (drift.magnitude() > acceptable_drift)