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/media/capture/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
,
17 ASSERT_TRUE(sampler
->AddEventAndConsiderSampling(*t
));
18 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
19 sampler
->RecordSample();
20 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
21 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
23 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
26 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync
,
27 SmoothEventSampler
* sampler
,
29 ASSERT_FALSE(sampler
->AddEventAndConsiderSampling(*t
));
30 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
31 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
33 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
));
36 void TimeTicksFromString(const char* string
, base::TimeTicks
* t
) {
38 ASSERT_TRUE(base::Time::FromString(string
, &time
));
39 *t
= base::TimeTicks::UnixEpoch() + (time
- base::Time::UnixEpoch());
42 void TestRedundantCaptureStrategy(base::TimeDelta capture_period
,
43 int redundant_capture_goal
,
44 SmoothEventSampler
* sampler
,
46 // Before any events have been considered, we're overdue for sampling.
47 ASSERT_TRUE(sampler
->IsOverdueForSamplingAt(*t
));
49 // Consider the first event. We want to sample that.
50 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
51 ASSERT_TRUE(sampler
->AddEventAndConsiderSampling(*t
));
52 ASSERT_TRUE(sampler
->HasUnrecordedEvent());
53 sampler
->RecordSample();
54 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
56 // After more than one capture period has passed without considering an event,
57 // we should repeatedly be overdue for sampling. However, once the redundant
58 // capture goal is achieved, we should no longer be overdue for sampling.
59 *t
+= capture_period
* 4;
60 for (int i
= 0; i
< redundant_capture_goal
; i
++) {
61 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
62 ASSERT_FALSE(sampler
->HasUnrecordedEvent());
63 ASSERT_TRUE(sampler
->IsOverdueForSamplingAt(*t
))
64 << "Should sample until redundant capture goal is hit";
65 sampler
->RecordSample();
66 *t
+= capture_period
; // Timer fires once every capture period.
68 ASSERT_FALSE(sampler
->IsOverdueForSamplingAt(*t
))
69 << "Should not be overdue once redundant capture goal achieved.";
72 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains
73 // much more comprehensive before/after/edge-case scenarios than the others.
74 TEST(SmoothEventSamplerTest
, Sample60HertzAt30Hertz
) {
75 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
76 const int redundant_capture_goal
= 200;
77 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 60;
79 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
81 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
83 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
86 // Steady state, we should capture every other vsync, indefinitely.
87 for (int i
= 0; i
< 100; i
++) {
88 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
89 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
90 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
93 // Now pretend we're limited by backpressure in the pipeline. In this scenario
94 // case we are adding events but not sampling them.
95 for (int i
= 0; i
< 20; i
++) {
96 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
97 ASSERT_EQ(i
>= 7, sampler
.IsOverdueForSamplingAt(t
));
98 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
99 ASSERT_TRUE(sampler
.HasUnrecordedEvent());
103 // Now suppose we can sample again. We should be back in the steady state,
104 // but at a different phase.
105 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
106 for (int i
= 0; i
< 100; i
++) {
107 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
108 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
109 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
113 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
114 TEST(SmoothEventSamplerTest
, Sample50HertzAt30Hertz
) {
115 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
116 const int redundant_capture_goal
= 2;
117 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 50;
119 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
121 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
123 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
126 // Steady state, we should capture 1st, 2nd and 4th frames out of every five
127 // frames, indefinitely.
128 for (int i
= 0; i
< 100; i
++) {
129 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
130 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
131 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
132 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
133 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
134 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
137 // Now pretend we're limited by backpressure in the pipeline. In this scenario
138 // case we are adding events but not sampling them.
139 for (int i
= 0; i
< 12; i
++) {
140 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
141 ASSERT_EQ(i
>= 5, sampler
.IsOverdueForSamplingAt(t
));
142 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
146 // Now suppose we can sample again. We should be back in the steady state
148 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
149 for (int i
= 0; i
< 100; i
++) {
150 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
151 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
152 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
153 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
154 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
155 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
159 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
160 TEST(SmoothEventSamplerTest
, Sample75HertzAt30Hertz
) {
161 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
162 const int redundant_capture_goal
= 32;
163 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 75;
165 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
167 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
169 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
172 // Steady state, we should capture 1st and 3rd frames out of every five
173 // frames, indefinitely.
174 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
175 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
176 for (int i
= 0; i
< 100; i
++) {
177 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
178 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
179 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
180 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
181 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
182 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
185 // Now pretend we're limited by backpressure in the pipeline. In this scenario
186 // case we are adding events but not sampling them.
187 for (int i
= 0; i
< 20; i
++) {
188 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
189 ASSERT_EQ(i
>= 8, sampler
.IsOverdueForSamplingAt(t
));
190 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
194 // Now suppose we can sample again. We capture the next frame, and not the one
195 // after that, and then we're back in the steady state again.
196 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
197 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
198 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
199 for (int i
= 0; i
< 100; i
++) {
200 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
201 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
202 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
203 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
204 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
205 SteadyStateNoSampleAndAdvance(vsync
, &sampler
, &t
);
209 // 30Hz sampled at 30Hz should produce 30Hz.
210 TEST(SmoothEventSamplerTest
, Sample30HertzAt30Hertz
) {
211 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
212 const int redundant_capture_goal
= 1;
213 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 30;
215 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
217 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
219 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
222 // Steady state, we should capture every vsync, indefinitely.
223 for (int i
= 0; i
< 200; i
++) {
224 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
225 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
228 // Now pretend we're limited by backpressure in the pipeline. In this scenario
229 // case we are adding events but not sampling them.
230 for (int i
= 0; i
< 7; i
++) {
231 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
232 ASSERT_EQ(i
>= 3, sampler
.IsOverdueForSamplingAt(t
));
233 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
237 // Now suppose we can sample again. We should be back in the steady state.
238 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
239 for (int i
= 0; i
< 100; i
++) {
240 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
241 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
245 // 24Hz sampled at 30Hz should produce 24Hz.
246 TEST(SmoothEventSamplerTest
, Sample24HertzAt30Hertz
) {
247 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
248 const int redundant_capture_goal
= 333;
249 const base::TimeDelta vsync
= base::TimeDelta::FromSeconds(1) / 24;
251 SmoothEventSampler
sampler(capture_period
, true, redundant_capture_goal
);
253 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
255 TestRedundantCaptureStrategy(capture_period
, redundant_capture_goal
,
258 // Steady state, we should capture every vsync, indefinitely.
259 for (int i
= 0; i
< 200; i
++) {
260 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
261 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
264 // Now pretend we're limited by backpressure in the pipeline. In this scenario
265 // case we are adding events but not sampling them.
266 for (int i
= 0; i
< 7; i
++) {
267 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
268 ASSERT_EQ(i
>= 3, sampler
.IsOverdueForSamplingAt(t
));
269 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
273 // Now suppose we can sample again. We should be back in the steady state.
274 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
));
275 for (int i
= 0; i
< 100; i
++) {
276 SCOPED_TRACE(base::StringPrintf("Iteration %d", i
));
277 SteadyStateSampleAndAdvance(vsync
, &sampler
, &t
);
281 TEST(SmoothEventSamplerTest
, DoubleDrawAtOneTimeStillDirties
) {
282 const base::TimeDelta capture_period
= base::TimeDelta::FromSeconds(1) / 30;
283 const base::TimeDelta overdue_period
= base::TimeDelta::FromSeconds(1);
285 SmoothEventSampler
sampler(capture_period
, true, 1);
287 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
289 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
290 sampler
.RecordSample();
291 ASSERT_FALSE(sampler
.IsOverdueForSamplingAt(t
))
292 << "Sampled last event; should not be dirty.";
295 // Now simulate 2 events with the same clock value.
296 ASSERT_TRUE(sampler
.AddEventAndConsiderSampling(t
));
297 sampler
.RecordSample();
298 ASSERT_FALSE(sampler
.AddEventAndConsiderSampling(t
))
299 << "Two events at same time -- expected second not to be sampled.";
300 ASSERT_TRUE(sampler
.IsOverdueForSamplingAt(t
+ overdue_period
))
301 << "Second event should dirty the capture state.";
302 sampler
.RecordSample();
303 ASSERT_FALSE(sampler
.IsOverdueForSamplingAt(t
+ overdue_period
));
306 TEST(SmoothEventSamplerTest
, FallbackToPollingIfUpdatesUnreliable
) {
307 const base::TimeDelta timer_interval
= base::TimeDelta::FromSeconds(1) / 30;
309 SmoothEventSampler
should_not_poll(timer_interval
, true, 1);
310 SmoothEventSampler
should_poll(timer_interval
, false, 1);
312 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
314 // Do one round of the "happy case" where an event was received and
315 // RecordSample() was called by the client.
316 ASSERT_TRUE(should_not_poll
.AddEventAndConsiderSampling(t
));
317 ASSERT_TRUE(should_poll
.AddEventAndConsiderSampling(t
));
318 should_not_poll
.RecordSample();
319 should_poll
.RecordSample();
321 // One time period ahead, neither sampler says we're overdue.
322 for (int i
= 0; i
< 3; i
++) {
324 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
325 << "Sampled last event; should not be dirty.";
326 ASSERT_FALSE(should_poll
.IsOverdueForSamplingAt(t
))
327 << "Dirty interval has not elapsed yet.";
330 // Next time period ahead, both samplers say we're overdue. The non-polling
331 // sampler is returning true here because it has been configured to allow one
332 // redundant capture.
334 ASSERT_TRUE(should_not_poll
.IsOverdueForSamplingAt(t
))
335 << "Sampled last event; is dirty one time only to meet redundancy goal.";
336 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
337 << "If updates are unreliable, must fall back to polling when idle.";
338 should_not_poll
.RecordSample();
339 should_poll
.RecordSample();
341 // Forever more, the non-polling sampler returns false while the polling one
343 for (int i
= 0; i
< 100; ++i
) {
345 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
346 << "Sampled last event; should not be dirty.";
347 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
348 << "If updates are unreliable, must fall back to polling when idle.";
349 should_poll
.RecordSample();
351 t
+= timer_interval
/ 3;
352 ASSERT_FALSE(should_not_poll
.IsOverdueForSamplingAt(t
))
353 << "Sampled last event; should not be dirty.";
354 ASSERT_TRUE(should_poll
.IsOverdueForSamplingAt(t
))
355 << "If updates are unreliable, must fall back to polling when idle.";
356 should_poll
.RecordSample();
364 void ReplayCheckingSamplerDecisions(const DataPoint
* data_points
,
365 size_t num_data_points
,
366 SmoothEventSampler
* sampler
) {
368 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t
);
369 for (size_t i
= 0; i
< num_data_points
; ++i
) {
370 t
+= base::TimeDelta::FromMicroseconds(
371 static_cast<int64
>(data_points
[i
].increment_ms
* 1000));
372 ASSERT_EQ(data_points
[i
].should_capture
,
373 sampler
->AddEventAndConsiderSampling(t
))
374 << "at data_points[" << i
<< ']';
375 if (data_points
[i
].should_capture
)
376 sampler
->RecordSample();
380 TEST(SmoothEventSamplerTest
, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz
) {
381 // Actual capturing of timing data: Initial instability as a 24 FPS video was
382 // started from a still screen, then clearly followed by steady-state.
383 static const DataPoint data_points
[] = {
384 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
385 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
386 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
387 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
388 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
389 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
390 { false, 0 }, { true, 50.161 }, { false, 0 }, { true, 33.44 },
391 { true, 16.72 }, { false, 16.721 }, { true, 66.881 }, { false, 0 },
392 { true, 33.441 }, { true, 16.72 }, { true, 50.16 }, { true, 16.72 },
393 { false, 16.721 }, { true, 50.161 }, { true, 50.16 }, { false, 0 },
394 { true, 33.441 }, { true, 50.337 }, { true, 50.183 }, { true, 16.722 },
395 { true, 50.161 }, { true, 33.441 }, { true, 50.16 }, { true, 33.441 },
396 { true, 50.16 }, { true, 33.441 }, { true, 50.16 }, { true, 33.44 },
397 { true, 50.161 }, { true, 50.16 }, { true, 33.44 }, { true, 33.441 },
398 { true, 50.16 }, { true, 50.161 }, { true, 33.44 }, { true, 33.441 },
399 { true, 50.16 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
400 { true, 50.161 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
401 { true, 83.601 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }
404 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
405 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
408 TEST(SmoothEventSamplerTest
, DrawingAt30FpsWith60HzVsyncSampledAt30Hertz
) {
409 // Actual capturing of timing data: Initial instability as a 30 FPS video was
410 // started from a still screen, then followed by steady-state. Drawing
411 // framerate from the video rendering was a bit volatile, but averaged 30 FPS.
412 static const DataPoint data_points
[] = {
413 { true, 2407.69 }, { true, 16.733 }, { true, 217.362 }, { true, 33.441 },
414 { true, 33.44 }, { true, 33.44 }, { true, 33.441 }, { true, 33.44 },
415 { true, 33.44 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 },
416 { true, 16.721 }, { true, 33.44 }, { false, 0 }, { true, 50.161 },
417 { true, 50.16 }, { false, 0 }, { true, 50.161 }, { true, 33.44 },
418 { true, 16.72 }, { false, 0 }, { false, 16.72 }, { true, 66.881 },
419 { false, 0 }, { true, 33.44 }, { true, 16.72 }, { true, 50.161 },
420 { false, 0 }, { true, 33.538 }, { true, 33.526 }, { true, 33.447 },
421 { true, 33.445 }, { true, 33.441 }, { true, 16.721 }, { true, 33.44 },
422 { true, 33.44 }, { true, 50.161 }, { true, 16.72 }, { true, 33.44 },
423 { true, 33.441 }, { true, 33.44 }, { false, 0 }, { false, 16.72 },
424 { true, 66.881 }, { true, 16.72 }, { false, 16.72 }, { true, 50.16 },
425 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 33.44 },
426 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { false, 0 },
427 { true, 33.44 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
428 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 66.88 },
429 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
430 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
431 { true, 16.72 }, { true, 50.161 }, { false, 0 }, { true, 50.16 },
432 { false, 0.001 }, { true, 16.721 }, { true, 66.88 }, { true, 33.44 },
433 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
434 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 66.881 },
435 { true, 33.44 }, { true, 16.72 }, { true, 33.441 }, { false, 16.72 },
436 { true, 66.88 }, { true, 16.721 }, { true, 50.16 }, { true, 33.44 },
437 { true, 16.72 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 }
440 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
441 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
444 TEST(SmoothEventSamplerTest
, DrawingAt60FpsWith60HzVsyncSampledAt30Hertz
) {
445 // Actual capturing of timing data: WebGL Acquarium demo
446 // (http://webglsamples.googlecode.com/hg/aquarium/aquarium.html) which ran
447 // between 55-60 FPS in the steady-state.
448 static const DataPoint data_points
[] = {
449 { true, 16.72 }, { true, 16.72 }, { true, 4163.29 }, { true, 50.193 },
450 { true, 117.041 }, { true, 50.161 }, { true, 50.16 }, { true, 33.441 },
451 { true, 50.16 }, { true, 33.44 }, { false, 0 }, { false, 0 },
452 { true, 50.161 }, { true, 83.601 }, { true, 50.16 }, { true, 16.72 },
453 { true, 33.441 }, { false, 16.72 }, { true, 50.16 }, { true, 16.72 },
454 { false, 0.001 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
455 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
456 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
457 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
458 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
459 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
460 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 33.44 },
461 { false, 0 }, { true, 16.721 }, { true, 50.161 }, { false, 0 },
462 { true, 33.44 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
463 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
464 { true, 50.16 }, { false, 0 }, { true, 16.721 }, { true, 33.44 },
465 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
466 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
467 { false, 0 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
468 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
469 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { true, 33.441 },
470 { false, 0 }, { true, 33.44 }, { true, 33.441 }, { false, 0 },
471 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
472 { true, 16.721 }, { true, 50.161 }, { false, 0 }, { true, 16.72 },
473 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 33.44 },
474 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
475 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
476 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
477 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
478 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
479 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
482 SmoothEventSampler
sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
483 ReplayCheckingSamplerDecisions(data_points
, arraysize(data_points
), &sampler
);
487 } // namespace content