1 // Copyright (c) 2013 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.
5 #include "content/browser/renderer_host/media/video_capture_oracle.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/time/time.h"
9 #include "testing/gtest/include/gtest/gtest.h"
14 void SteadyStateSampleAndAdvance(base::TimeDelta vsync
,
15 SmoothEventSampler
* sampler
, base::Time
* t
) {
16 ASSERT_TRUE(sampler
->AddEventAndConsiderSampling(*t
));
17 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
18 sampler
->RecordSample();
19 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
20 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
22 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
25 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync
,
26 SmoothEventSampler
* sampler
, base::Time
* t
) {
27 ASSERT_FALSE(sampler
->AddEventAndConsiderSampling(*t
));
28 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
29 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
31 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
34 void TestRedundantCaptureStrategy(base::TimeDelta capture_period
,
35 int redundant_capture_goal
,
36 SmoothEventSampler
* sampler
, base::Time
* t
) {
37 // Before any events have been considered, we're overdue for sampling.
38 ASSERT_TRUE(sampler
->IsOverdueForSamplingAt(*t
));
40 // Consider the first event. We want to sample that.
41 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
42 ASSERT_TRUE(sampler
->AddEventAndConsiderSampling(*t
));
43 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
44 sampler
->RecordSample();
45 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
47 // After more than one capture period has passed without considering an event,
48 // we should repeatedly be overdue for sampling. However, once the redundant
49 // capture goal is achieved, we should no longer be overdue for sampling.
50 *t
+= capture_period
* 4;
51 for (int i
= 0; i
< redundant_capture_goal
; i
++) {
52 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
53 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
54 ASSERT_TRUE(sampler
->IsOverdueForSamplingAt(*t
))
55 << "Should sample until redundant capture goal is hit";
56 sampler
->RecordSample();
57 *t
+= capture_period
; // Timer fires once every capture period.
59 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
))
60 << "Should not be overdue once redundant capture goal achieved.";
63 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains
64 // much more comprehensive before/after/edge-case scenarios than the others.
65 TEST(SmoothEventSamplerTest
, Sample60HertzAt30Hertz
) {
66 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
67 const int redundant_capture_goal
= 200;
68 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 60;
70 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
72 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
74 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
77 // Steady state, we should capture every other vsync, indefinitely.
78 for (int i
= 0; i
< 100; i
++) {
79 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
80 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
81 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
84 // Now pretend we're limited by backpressure in the pipeline. In this scenario
85 // case we are adding events but not sampling them.
86 for (int i
= 0; i
< 20; i
++) {
87 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
88 ASSERT_EQ(i
>= 7, sampler
.IsOverdueForSamplingAt(t
));
89 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
90 ASSERT_TRUE(sampler
.HasUnrecordedEvent());
94 // Now suppose we can sample again. We should be back in the steady state,
95 // but at a different phase.
96 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
97 for (int i
= 0; i
< 100; i
++) {
98 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
99 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
100 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
104 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
105 TEST(SmoothEventSamplerTest
, Sample50HertzAt30Hertz
) {
106 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
107 const int redundant_capture_goal
= 2;
108 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 50;
110 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
112 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
114 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
117 // Steady state, we should capture 1st, 2nd and 4th frames out of every five
118 // frames, indefinitely.
119 for (int i
= 0; i
< 100; i
++) {
120 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
121 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
122 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
123 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
124 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
125 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
128 // Now pretend we're limited by backpressure in the pipeline. In this scenario
129 // case we are adding events but not sampling them.
130 for (int i
= 0; i
< 12; i
++) {
131 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
132 ASSERT_EQ(i
>= 5, sampler
.IsOverdueForSamplingAt(t
));
133 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
137 // Now suppose we can sample again. We should be back in the steady state
139 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
140 for (int i
= 0; i
< 100; i
++) {
141 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
142 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
143 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
144 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
145 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
146 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
150 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
151 TEST(SmoothEventSamplerTest
, Sample75HertzAt30Hertz
) {
152 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
153 const int redundant_capture_goal
= 32;
154 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 75;
156 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
158 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
160 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
163 // Steady state, we should capture 1st and 3rd frames out of every five
164 // frames, indefinitely.
165 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
166 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
167 for (int i
= 0; i
< 100; i
++) {
168 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
169 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
170 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
171 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
172 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
173 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
176 // Now pretend we're limited by backpressure in the pipeline. In this scenario
177 // case we are adding events but not sampling them.
178 for (int i
= 0; i
< 20; i
++) {
179 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
180 ASSERT_EQ(i
>= 8, sampler
.IsOverdueForSamplingAt(t
));
181 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
185 // Now suppose we can sample again. We capture the next frame, and not the one
186 // after that, and then we're back in the steady state again.
187 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
188 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
189 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
190 for (int i
= 0; i
< 100; i
++) {
191 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
192 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
193 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
194 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
195 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
196 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
200 // 30Hz sampled at 30Hz should produce 30Hz.
201 TEST(SmoothEventSamplerTest
, Sample30HertzAt30Hertz
) {
202 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
203 const int redundant_capture_goal
= 1;
204 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 30;
206 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
208 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
210 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
213 // Steady state, we should capture every vsync, indefinitely.
214 for (int i
= 0; i
< 200; i
++) {
215 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
216 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
219 // Now pretend we're limited by backpressure in the pipeline. In this scenario
220 // case we are adding events but not sampling them.
221 for (int i
= 0; i
< 7; i
++) {
222 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
223 ASSERT_EQ(i
>= 3, sampler
.IsOverdueForSamplingAt(t
));
224 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
228 // Now suppose we can sample again. We should be back in the steady state.
229 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
230 for (int i
= 0; i
< 100; i
++) {
231 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
232 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
236 // 24Hz sampled at 30Hz should produce 24Hz.
237 TEST(SmoothEventSamplerTest
, Sample24HertzAt30Hertz
) {
238 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
239 const int redundant_capture_goal
= 333;
240 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 24;
242 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
244 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
246 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
249 // Steady state, we should capture every vsync, indefinitely.
250 for (int i
= 0; i
< 200; i
++) {
251 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
252 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
255 // Now pretend we're limited by backpressure in the pipeline. In this scenario
256 // case we are adding events but not sampling them.
257 for (int i
= 0; i
< 7; i
++) {
258 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
259 ASSERT_EQ(i
>= 3, sampler
.IsOverdueForSamplingAt(t
));
260 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
264 // Now suppose we can sample again. We should be back in the steady state.
265 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
266 for (int i
= 0; i
< 100; i
++) {
267 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
268 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
272 TEST(SmoothEventSamplerTest
, DoubleDrawAtOneTimeStillDirties
) {
273 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
274 const base::TimeDelta overdue_period
= base::TimeDelta::FromSeconds(1);
276 SmoothEventSampler
sampler(capture_period
, true, 1);
278 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
280 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
281 sampler
.RecordSample();
282 ASSERT_FALSE(sampler
.IsOverdueForSamplingAt(t
))
283 << "Sampled last event; should not be dirty.";
286 // Now simulate 2 events with the same clock value.
287 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
288 sampler
.RecordSample();
289 ASSERT_FALSE(sampler
.AddEventAndConsiderSampling(t
))
290 << "Two events at same time -- expected second not to be sampled.";
291 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
+ overdue_period
))
292 << "Second event should dirty the capture state.";
293 sampler
.RecordSample();
294 ASSERT_FALSE(sampler
.IsOverdueForSamplingAt(t
+ overdue_period
));
297 TEST(SmoothEventSamplerTest
, FallbackToPollingIfUpdatesUnreliable
) {
298 const base::TimeDelta timer_interval
= base::TimeDelta::FromSeconds(1) / 30;
300 SmoothEventSampler
should_not_poll(timer_interval
, true, 1);
301 SmoothEventSampler
should_poll(timer_interval
, false, 1);
303 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
305 // Do one round of the "happy case" where an event was received and
306 // RecordSample() was called by the client.
307 ASSERT_TRUE(should_not_poll
.AddEventAndConsiderSampling(t
));
308 ASSERT_TRUE(should_poll
.AddEventAndConsiderSampling(t
));
309 should_not_poll
.RecordSample();
310 should_poll
.RecordSample();
312 // One time period ahead, neither sampler says we're overdue.
313 for (int i
= 0; i
< 3; i
++) {
315 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
316 << "Sampled last event; should not be dirty.";
317 ASSERT_FALSE(should_poll
.IsOverdueForSamplingAt(t
))
318 << "Dirty interval has not elapsed yet.";
321 // Next time period ahead, both samplers say we're overdue. The non-polling
322 // sampler is returning true here because it has been configured to allow one
323 // redundant capture.
325 ASSERT_TRUE(should_not_poll
.IsOverdueForSamplingAt(t
))
326 << "Sampled last event; is dirty one time only to meet redundancy goal.";
327 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
328 << "If updates are unreliable, must fall back to polling when idle.";
329 should_not_poll
.RecordSample();
330 should_poll
.RecordSample();
332 // Forever more, the non-polling sampler returns false while the polling one
334 for (int i
= 0; i
< 100; ++i
) {
336 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
337 << "Sampled last event; should not be dirty.";
338 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
339 << "If updates are unreliable, must fall back to polling when idle.";
340 should_poll
.RecordSample();
342 t
+= timer_interval
/ 3;
343 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
344 << "Sampled last event; should not be dirty.";
345 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
346 << "If updates are unreliable, must fall back to polling when idle.";
347 should_poll
.RecordSample();
355 void ReplayCheckingSamplerDecisions(const DataPoint
* data_points
,
356 size_t num_data_points
,
357 SmoothEventSampler
* sampler
) {
359 ASSERT_TRUE(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
));
360 for (size_t i
= 0; i
< num_data_points
; ++i
) {
361 t
+= base::TimeDelta::FromMicroseconds(
362 static_cast<int64
>(data_points
[i
].increment_ms
* 1000));
363 ASSERT_EQ(data_points
[i
].should_capture
,
364 sampler
->AddEventAndConsiderSampling(t
))
365 << "at data_points[" << i
<< ']';
366 if (data_points
[i
].should_capture
)
367 sampler
->RecordSample();
371 TEST(SmoothEventSamplerTest
, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz
) {
372 // Actual capturing of timing data: Initial instability as a 24 FPS video was
373 // started from a still screen, then clearly followed by steady-state.
374 static const DataPoint data_points
[] = {
375 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
376 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
377 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
378 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
379 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
380 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
381 { false, 0 }, { true, 50.161 }, { false, 0 }, { true, 33.44 },
382 { true, 16.72 }, { false, 16.721 }, { true, 66.881 }, { false, 0 },
383 { true, 33.441 }, { true, 16.72 }, { true, 50.16 }, { true, 16.72 },
384 { false, 16.721 }, { true, 50.161 }, { true, 50.16 }, { false, 0 },
385 { true, 33.441 }, { true, 50.337 }, { true, 50.183 }, { true, 16.722 },
386 { true, 50.161 }, { true, 33.441 }, { true, 50.16 }, { true, 33.441 },
387 { true, 50.16 }, { true, 33.441 }, { true, 50.16 }, { true, 33.44 },
388 { true, 50.161 }, { true, 50.16 }, { true, 33.44 }, { true, 33.441 },
389 { true, 50.16 }, { true, 50.161 }, { true, 33.44 }, { true, 33.441 },
390 { true, 50.16 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
391 { true, 50.161 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
392 { true, 83.601 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }
395 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
396 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
399 TEST(SmoothEventSamplerTest
, DrawingAt30FpsWith60HzVsyncSampledAt30Hertz
) {
400 // Actual capturing of timing data: Initial instability as a 30 FPS video was
401 // started from a still screen, then followed by steady-state. Drawing
402 // framerate from the video rendering was a bit volatile, but averaged 30 FPS.
403 static const DataPoint data_points
[] = {
404 { true, 2407.69 }, { true, 16.733 }, { true, 217.362 }, { true, 33.441 },
405 { true, 33.44 }, { true, 33.44 }, { true, 33.441 }, { true, 33.44 },
406 { true, 33.44 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 },
407 { true, 16.721 }, { true, 33.44 }, { false, 0 }, { true, 50.161 },
408 { true, 50.16 }, { false, 0 }, { true, 50.161 }, { true, 33.44 },
409 { true, 16.72 }, { false, 0 }, { false, 16.72 }, { true, 66.881 },
410 { false, 0 }, { true, 33.44 }, { true, 16.72 }, { true, 50.161 },
411 { false, 0 }, { true, 33.538 }, { true, 33.526 }, { true, 33.447 },
412 { true, 33.445 }, { true, 33.441 }, { true, 16.721 }, { true, 33.44 },
413 { true, 33.44 }, { true, 50.161 }, { true, 16.72 }, { true, 33.44 },
414 { true, 33.441 }, { true, 33.44 }, { false, 0 }, { false, 16.72 },
415 { true, 66.881 }, { true, 16.72 }, { false, 16.72 }, { true, 50.16 },
416 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 33.44 },
417 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { false, 0 },
418 { true, 33.44 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
419 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 66.88 },
420 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
421 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
422 { true, 16.72 }, { true, 50.161 }, { false, 0 }, { true, 50.16 },
423 { false, 0.001 }, { true, 16.721 }, { true, 66.88 }, { true, 33.44 },
424 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
425 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 66.881 },
426 { true, 33.44 }, { true, 16.72 }, { true, 33.441 }, { false, 16.72 },
427 { true, 66.88 }, { true, 16.721 }, { true, 50.16 }, { true, 33.44 },
428 { true, 16.72 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 }
431 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
432 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
435 TEST(SmoothEventSamplerTest
, DrawingAt60FpsWith60HzVsyncSampledAt30Hertz
) {
436 // Actual capturing of timing data: WebGL Acquarium demo
437 // (http://webglsamples.googlecode.com/hg/aquarium/aquarium.html) which ran
438 // between 55-60 FPS in the steady-state.
439 static const DataPoint data_points
[] = {
440 { true, 16.72 }, { true, 16.72 }, { true, 4163.29 }, { true, 50.193 },
441 { true, 117.041 }, { true, 50.161 }, { true, 50.16 }, { true, 33.441 },
442 { true, 50.16 }, { true, 33.44 }, { false, 0 }, { false, 0 },
443 { true, 50.161 }, { true, 83.601 }, { true, 50.16 }, { true, 16.72 },
444 { true, 33.441 }, { false, 16.72 }, { true, 50.16 }, { true, 16.72 },
445 { false, 0.001 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
446 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
447 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
448 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
449 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
450 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
451 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 33.44 },
452 { false, 0 }, { true, 16.721 }, { true, 50.161 }, { false, 0 },
453 { true, 33.44 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
454 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
455 { true, 50.16 }, { false, 0 }, { true, 16.721 }, { true, 33.44 },
456 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
457 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
458 { false, 0 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
459 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
460 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { true, 33.441 },
461 { false, 0 }, { true, 33.44 }, { true, 33.441 }, { false, 0 },
462 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
463 { true, 16.721 }, { true, 50.161 }, { false, 0 }, { true, 16.72 },
464 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 33.44 },
465 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
466 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
467 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
468 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
469 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
470 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
473 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
474 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
478 } // namespace content