video_cadence_estimator_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/filters/video_cadence_estimator.h"
  5. #include <math.h>
  6. #include <stddef.h>
  7. #include <memory>
  8. #include "base/numerics/safe_conversions.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_split.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/test/scoped_feature_list.h"
  13. #include "base/time/time.h"
  14. #include "media/base/media_switches.h"
  15. #include "testing/gtest/include/gtest/gtest.h"
  16. namespace media {
  17. // See VideoCadenceEstimator header for more details.
  18. constexpr auto kMinimumAcceptableTimeBetweenGlitches = base::Seconds(8);
  19. // Slows down the given |fps| according to NTSC field reduction standards; see
  20. // http://en.wikipedia.org/wiki/Frame_rate#Digital_video_and_television
  21. static double NTSC(double fps) {
  22. return fps / 1.001;
  23. }
  24. static base::TimeDelta Interval(double hertz) {
  25. return base::Seconds(1.0 / hertz);
  26. }
  27. std::vector<int> CreateCadenceFromString(const std::string& cadence) {
  28. CHECK_EQ('[', cadence.front());
  29. CHECK_EQ(']', cadence.back());
  30. std::vector<int> result;
  31. for (const std::string& token :
  32. base::SplitString(cadence.substr(1, cadence.length() - 2),
  33. ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
  34. int cadence_value = 0;
  35. CHECK(base::StringToInt(token, &cadence_value)) << token;
  36. result.push_back(cadence_value);
  37. }
  38. return result;
  39. }
  40. static void VerifyCadenceVectorWithCustomDeviationAndDrift(
  41. VideoCadenceEstimator* estimator,
  42. double frame_hertz,
  43. double render_hertz,
  44. base::TimeDelta deviation,
  45. base::TimeDelta acceptable_drift,
  46. const std::string& expected_cadence) {
  47. SCOPED_TRACE(base::StringPrintf("Checking %.03f fps into %0.03f", frame_hertz,
  48. render_hertz));
  49. const std::vector<int> expected_cadence_vector =
  50. CreateCadenceFromString(expected_cadence);
  51. estimator->Reset();
  52. const bool cadence_changed = estimator->UpdateCadenceEstimate(
  53. Interval(render_hertz), Interval(frame_hertz), deviation,
  54. acceptable_drift);
  55. EXPECT_EQ(cadence_changed, estimator->has_cadence());
  56. EXPECT_EQ(expected_cadence_vector.empty(), !estimator->has_cadence());
  57. // Nothing further to test.
  58. if (expected_cadence_vector.empty() || !estimator->has_cadence())
  59. return;
  60. EXPECT_EQ(expected_cadence_vector.size(),
  61. estimator->cadence_size_for_testing());
  62. // Spot two cycles of the cadence.
  63. for (size_t i = 0; i < expected_cadence_vector.size() * 2; ++i) {
  64. ASSERT_EQ(expected_cadence_vector[i % expected_cadence_vector.size()],
  65. estimator->GetCadenceForFrame(i));
  66. }
  67. }
  68. static void VerifyCadenceVectorWithCustomDrift(
  69. VideoCadenceEstimator* estimator,
  70. double frame_hertz,
  71. double render_hertz,
  72. base::TimeDelta acceptable_drift,
  73. const std::string& expected_cadence) {
  74. VerifyCadenceVectorWithCustomDeviationAndDrift(
  75. estimator, frame_hertz, render_hertz, base::TimeDelta(), acceptable_drift,
  76. expected_cadence);
  77. }
  78. static void VerifyCadenceVectorWithCustomDeviation(
  79. VideoCadenceEstimator* estimator,
  80. double frame_hertz,
  81. double render_hertz,
  82. base::TimeDelta deviation,
  83. const std::string& expected_cadence) {
  84. const base::TimeDelta acceptable_drift =
  85. std::max(Interval(frame_hertz) / 2, Interval(render_hertz));
  86. VerifyCadenceVectorWithCustomDeviationAndDrift(
  87. estimator, frame_hertz, render_hertz, deviation, acceptable_drift,
  88. expected_cadence);
  89. }
  90. static void VerifyCadenceVector(VideoCadenceEstimator* estimator,
  91. double frame_hertz,
  92. double render_hertz,
  93. const std::string& expected_cadence) {
  94. const base::TimeDelta acceptable_drift =
  95. std::max(Interval(frame_hertz) / 2, Interval(render_hertz));
  96. VerifyCadenceVectorWithCustomDeviationAndDrift(
  97. estimator, frame_hertz, render_hertz, base::TimeDelta(), acceptable_drift,
  98. expected_cadence);
  99. }
  100. // Spot check common display and frame rate pairs for correctness.
  101. TEST(VideoCadenceEstimatorTest, CadenceCalculations) {
  102. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  103. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  104. const std::string kEmptyCadence = "[]";
  105. VerifyCadenceVector(&estimator, 1, NTSC(60), "[60]");
  106. VerifyCadenceVector(&estimator, 24, 60, "[3:2]");
  107. VerifyCadenceVector(&estimator, NTSC(24), 60, "[3:2]");
  108. VerifyCadenceVector(&estimator, 24, NTSC(60), "[3:2]");
  109. VerifyCadenceVector(&estimator, 25, 60, "[2:3:2:3:2]");
  110. VerifyCadenceVector(&estimator, NTSC(25), 60, "[2:3:2:3:2]");
  111. VerifyCadenceVector(&estimator, 25, NTSC(60), "[2:3:2:3:2]");
  112. VerifyCadenceVector(&estimator, 30, 60, "[2]");
  113. VerifyCadenceVector(&estimator, NTSC(30), 60, "[2]");
  114. VerifyCadenceVector(&estimator, 29.5, 60, kEmptyCadence);
  115. VerifyCadenceVector(&estimator, 50, 60, "[1:1:2:1:1]");
  116. VerifyCadenceVector(&estimator, NTSC(50), 60, "[1:1:2:1:1]");
  117. VerifyCadenceVector(&estimator, 50, NTSC(60), "[1:1:2:1:1]");
  118. VerifyCadenceVector(&estimator, NTSC(60), 60, "[1]");
  119. VerifyCadenceVector(&estimator, 60, NTSC(60), "[1]");
  120. VerifyCadenceVector(&estimator, 120, 60, "[1:0]");
  121. VerifyCadenceVector(&estimator, NTSC(120), 60, "[1:0]");
  122. VerifyCadenceVector(&estimator, 120, NTSC(60), "[1:0]");
  123. // Test cases for cadence below 1.
  124. VerifyCadenceVector(&estimator, 120, 24, "[1:0:0:0:0]");
  125. VerifyCadenceVector(&estimator, 120, 48, "[1:0:0:1:0]");
  126. VerifyCadenceVector(&estimator, 120, 72, "[1:0:1:0:1]");
  127. VerifyCadenceVector(&estimator, 90, 60, "[1:0:1]");
  128. // 50Hz is common in the EU.
  129. VerifyCadenceVector(&estimator, NTSC(24), 50, kEmptyCadence);
  130. VerifyCadenceVector(&estimator, 24, 50, kEmptyCadence);
  131. VerifyCadenceVector(&estimator, NTSC(25), 50, "[2]");
  132. VerifyCadenceVector(&estimator, 25, 50, "[2]");
  133. VerifyCadenceVector(&estimator, NTSC(30), 50, "[2:1:2]");
  134. VerifyCadenceVector(&estimator, 30, 50, "[2:1:2]");
  135. VerifyCadenceVector(&estimator, NTSC(60), 50, kEmptyCadence);
  136. VerifyCadenceVector(&estimator, 60, 50, kEmptyCadence);
  137. }
  138. // Check the extreme case that max_acceptable_drift is larger than
  139. // minimum_time_until_max_drift.
  140. TEST(VideoCadenceEstimatorTest, CadenceCalculationWithLargeDrift) {
  141. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  142. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  143. base::TimeDelta drift = base::Hours(1);
  144. VerifyCadenceVectorWithCustomDrift(&estimator, 1, NTSC(60), drift, "[60]");
  145. VerifyCadenceVectorWithCustomDrift(&estimator, 30, 60, drift, "[2]");
  146. VerifyCadenceVectorWithCustomDrift(&estimator, NTSC(30), 60, drift, "[2]");
  147. VerifyCadenceVectorWithCustomDrift(&estimator, 30, NTSC(60), drift, "[2]");
  148. VerifyCadenceVectorWithCustomDrift(&estimator, 25, 60, drift, "[2]");
  149. VerifyCadenceVectorWithCustomDrift(&estimator, NTSC(25), 60, drift, "[2]");
  150. VerifyCadenceVectorWithCustomDrift(&estimator, 25, NTSC(60), drift, "[2]");
  151. // Test cases for cadence below 1.
  152. VerifyCadenceVectorWithCustomDrift(&estimator, 120, 24, drift, "[1]");
  153. VerifyCadenceVectorWithCustomDrift(&estimator, 120, 48, drift, "[1]");
  154. VerifyCadenceVectorWithCustomDrift(&estimator, 120, 72, drift, "[1]");
  155. VerifyCadenceVectorWithCustomDrift(&estimator, 90, 60, drift, "[1]");
  156. }
  157. // Check the case that the estimator excludes variable FPS case from Cadence.
  158. TEST(VideoCadenceEstimatorTest, CadenceCalculationWithLargeDeviation) {
  159. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  160. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  161. const base::TimeDelta deviation = base::Milliseconds(30);
  162. VerifyCadenceVectorWithCustomDeviation(&estimator, 1, 60, deviation, "[]");
  163. VerifyCadenceVectorWithCustomDeviation(&estimator, 30, 60, deviation, "[]");
  164. VerifyCadenceVectorWithCustomDeviation(&estimator, 25, 60, deviation, "[]");
  165. // Test cases for cadence with low refresh rate.
  166. VerifyCadenceVectorWithCustomDeviation(&estimator, 60, 12, deviation,
  167. "[1:0:0:0:0]");
  168. }
  169. TEST(VideoCadenceEstimatorTest, CadenceVariesWithAcceptableDrift) {
  170. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  171. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  172. const base::TimeDelta render_interval = Interval(NTSC(60));
  173. const base::TimeDelta frame_interval = Interval(120);
  174. base::TimeDelta acceptable_drift = frame_interval / 2;
  175. EXPECT_FALSE(estimator.UpdateCadenceEstimate(
  176. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  177. EXPECT_FALSE(estimator.has_cadence());
  178. // Increasing the acceptable drift should be result in more permissive
  179. // detection of cadence.
  180. acceptable_drift = render_interval;
  181. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  182. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  183. EXPECT_TRUE(estimator.has_cadence());
  184. EXPECT_EQ("[1:0]", estimator.GetCadenceForTesting());
  185. }
  186. TEST(VideoCadenceEstimatorTest, CadenceVariesWithAcceptableGlitchTime) {
  187. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  188. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  189. const base::TimeDelta render_interval = Interval(NTSC(60));
  190. const base::TimeDelta frame_interval = Interval(120);
  191. const base::TimeDelta acceptable_drift = frame_interval / 2;
  192. EXPECT_FALSE(estimator.UpdateCadenceEstimate(
  193. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  194. EXPECT_FALSE(estimator.has_cadence());
  195. // Decreasing the acceptable glitch time should be result in more permissive
  196. // detection of cadence.
  197. VideoCadenceEstimator permissive_estimator(
  198. kMinimumAcceptableTimeBetweenGlitches / 2);
  199. permissive_estimator.set_cadence_hysteresis_threshold_for_testing(
  200. base::TimeDelta());
  201. EXPECT_TRUE(permissive_estimator.UpdateCadenceEstimate(
  202. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  203. EXPECT_TRUE(permissive_estimator.has_cadence());
  204. EXPECT_EQ("[1:0]", permissive_estimator.GetCadenceForTesting());
  205. }
  206. TEST(VideoCadenceEstimatorTest, CadenceHystersisPreventsOscillation) {
  207. VideoCadenceEstimator estimator(kMinimumAcceptableTimeBetweenGlitches);
  208. const base::TimeDelta render_interval = Interval(30);
  209. const base::TimeDelta frame_interval = Interval(60);
  210. const base::TimeDelta acceptable_drift = frame_interval / 2;
  211. estimator.set_cadence_hysteresis_threshold_for_testing(render_interval * 2);
  212. // Cadence hysteresis should prevent the cadence from taking effect yet.
  213. EXPECT_FALSE(estimator.UpdateCadenceEstimate(
  214. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  215. EXPECT_FALSE(estimator.has_cadence());
  216. // A second call should exceed cadence hysteresis and take into effect.
  217. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  218. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  219. EXPECT_TRUE(estimator.has_cadence());
  220. // One bad interval shouldn't cause cadence to drop
  221. EXPECT_FALSE(
  222. estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
  223. base::TimeDelta(), acceptable_drift));
  224. EXPECT_TRUE(estimator.has_cadence());
  225. // Resumption of cadence should clear bad interval count.
  226. EXPECT_FALSE(estimator.UpdateCadenceEstimate(
  227. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  228. EXPECT_TRUE(estimator.has_cadence());
  229. // So one more bad interval shouldn't cause cadence to drop
  230. EXPECT_FALSE(
  231. estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
  232. base::TimeDelta(), acceptable_drift));
  233. EXPECT_TRUE(estimator.has_cadence());
  234. // Two bad intervals should.
  235. EXPECT_TRUE(
  236. estimator.UpdateCadenceEstimate(render_interval, frame_interval * 0.75,
  237. base::TimeDelta(), acceptable_drift));
  238. EXPECT_FALSE(estimator.has_cadence());
  239. }
  240. void VerifyCadenceSequence(VideoCadenceEstimator* estimator,
  241. double frame_rate,
  242. double display_rate,
  243. std::vector<int> expected_cadence) {
  244. SCOPED_TRACE(base::StringPrintf("Checking %.03f fps into %0.03f", frame_rate,
  245. display_rate));
  246. const base::TimeDelta render_interval = Interval(display_rate);
  247. const base::TimeDelta frame_interval = Interval(frame_rate);
  248. const base::TimeDelta acceptable_drift =
  249. frame_interval < render_interval ? render_interval : frame_interval;
  250. const base::TimeDelta test_runtime = base::Seconds(10 * 60);
  251. const int test_frames = base::ClampFloor(test_runtime / frame_interval);
  252. estimator->Reset();
  253. EXPECT_TRUE(estimator->UpdateCadenceEstimate(
  254. render_interval, frame_interval, base::TimeDelta(), acceptable_drift));
  255. EXPECT_TRUE(estimator->has_cadence());
  256. for (auto i = 0u; i < expected_cadence.size(); i++) {
  257. ASSERT_EQ(expected_cadence[i], estimator->GetCadenceForFrame(i))
  258. << " i=" << i;
  259. }
  260. int total_display_cycles = 0;
  261. for (int i = 0; i < test_frames; i++) {
  262. total_display_cycles += estimator->GetCadenceForFrame(i);
  263. base::TimeDelta drift =
  264. (total_display_cycles * render_interval) - ((i + 1) * frame_interval);
  265. EXPECT_LE(drift.magnitude(), acceptable_drift)
  266. << " i=" << i << " time=" << (total_display_cycles * render_interval);
  267. if (drift.magnitude() > acceptable_drift)
  268. break;
  269. }
  270. }
  271. TEST(VideoCadenceEstimatorTest, BresenhamCadencePatterns) {
  272. base::test::ScopedFeatureList scoped_feature_list;
  273. scoped_feature_list.InitAndEnableFeature(media::kBresenhamCadence);
  274. VideoCadenceEstimator estimator(base::Seconds(1));
  275. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  276. VerifyCadenceSequence(&estimator, 30, 60,
  277. {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2});
  278. VerifyCadenceSequence(&estimator, NTSC(30), 60,
  279. {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2});
  280. VerifyCadenceSequence(&estimator, 30, NTSC(60),
  281. {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2});
  282. VerifyCadenceSequence(&estimator, 25, 60, {2, 3, 2, 3, 2, 2, 3, 2});
  283. VerifyCadenceSequence(&estimator, 24, 60, {3, 2, 3, 2, 3, 2, 3, 2});
  284. VerifyCadenceSequence(&estimator, NTSC(24), 60, {3, 2, 3, 2, 3, 2, 3, 2});
  285. VerifyCadenceSequence(&estimator, 24, NTSC(60), {2, 3, 2, 3, 2, 3, 2, 3, 2});
  286. VerifyCadenceSequence(&estimator, 24, 50,
  287. {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2});
  288. VerifyCadenceSequence(&estimator, NTSC(24), 50,
  289. {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2});
  290. VerifyCadenceSequence(&estimator, 30, 50, {2, 1, 2, 2, 1, 2, 2});
  291. VerifyCadenceSequence(&estimator, NTSC(30), 50, {2, 2, 1, 2, 2});
  292. VerifyCadenceSequence(&estimator, 120, 24, {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1});
  293. VerifyCadenceSequence(&estimator, 60, 50, {1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0});
  294. VerifyCadenceSequence(&estimator, 25, 50, {2, 2, 2, 2, 2, 2, 2, 2, 2});
  295. VerifyCadenceSequence(&estimator, 50, 25, {1, 0, 1, 0, 1, 0, 1, 0});
  296. VerifyCadenceSequence(&estimator, 120, 60, {1, 0, 1, 0, 1, 0, 1, 0});
  297. // Frame rate deviation is too high, refuse to provide cadence.
  298. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  299. Interval(60), Interval(30), base::Milliseconds(20), base::Seconds(100)));
  300. EXPECT_FALSE(estimator.has_cadence());
  301. // No cadence change for neglegable rate changes
  302. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  303. Interval(60), Interval(30), base::TimeDelta(), base::TimeDelta()));
  304. EXPECT_FALSE(estimator.UpdateCadenceEstimate(Interval(60 * 1.0001),
  305. Interval(30), base::TimeDelta(),
  306. base::TimeDelta()));
  307. }
  308. TEST(VideoCadenceEstimatorTest, BresenhamCadenceChange) {
  309. base::test::ScopedFeatureList scoped_feature_list;
  310. scoped_feature_list.InitAndEnableFeature(media::kBresenhamCadence);
  311. VideoCadenceEstimator estimator(base::Seconds(1));
  312. estimator.set_cadence_hysteresis_threshold_for_testing(base::TimeDelta());
  313. base::TimeDelta render_interval = Interval(60);
  314. base::TimeDelta frame_duration = Interval(24);
  315. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  316. render_interval, frame_duration, base::TimeDelta(), base::TimeDelta()));
  317. EXPECT_FALSE(estimator.UpdateCadenceEstimate(
  318. render_interval, frame_duration, base::TimeDelta(), base::TimeDelta()));
  319. for (double t = 0.0; t < 10.0; t += 0.1) {
  320. // +-100us drift of the rendering interval, a totally realistic thing.
  321. base::TimeDelta new_render_interval =
  322. render_interval + base::Microseconds(std::sin(t) * 100);
  323. EXPECT_FALSE(
  324. estimator.UpdateCadenceEstimate(new_render_interval, frame_duration,
  325. base::TimeDelta(), base::TimeDelta()))
  326. << "render interval: " << new_render_interval
  327. << " hz: " << new_render_interval.ToHz();
  328. }
  329. EXPECT_TRUE(estimator.UpdateCadenceEstimate(
  330. Interval(59), frame_duration, base::TimeDelta(), base::TimeDelta()));
  331. }
  332. } // namespace media