Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / media / capture / video_capture_oracle_unittest.cc
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.
4
5 #include "content/browser/media/capture/video_capture_oracle.h"
6
7 #include <cstdlib>
8 #include <utility>
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h"
16
17 namespace content {
18 namespace {
19
20 bool AddEventAndConsiderSampling(SmoothEventSampler* sampler,
21                                  base::TimeTicks event_time) {
22   sampler->ConsiderPresentationEvent(event_time);
23   return sampler->ShouldSample();
24 }
25
26 void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
27                                  SmoothEventSampler* sampler,
28                                  base::TimeTicks* t) {
29   ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
30   ASSERT_TRUE(sampler->HasUnrecordedEvent());
31   sampler->RecordSample();
32   ASSERT_FALSE(sampler->HasUnrecordedEvent());
33   ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
34   *t += vsync;
35   ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
36 }
37
38 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
39                                    SmoothEventSampler* sampler,
40                                    base::TimeTicks* t) {
41   ASSERT_FALSE(AddEventAndConsiderSampling(sampler, *t));
42   ASSERT_TRUE(sampler->HasUnrecordedEvent());
43   ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
44   *t += vsync;
45   ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
46 }
47
48 base::TimeTicks InitialTestTimeTicks() {
49   return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
50 }
51
52 void TestRedundantCaptureStrategy(base::TimeDelta capture_period,
53                                   int redundant_capture_goal,
54                                   SmoothEventSampler* sampler,
55                                   base::TimeTicks* t) {
56   // Before any events have been considered, we're overdue for sampling.
57   ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t));
58
59   // Consider the first event.  We want to sample that.
60   ASSERT_FALSE(sampler->HasUnrecordedEvent());
61   ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
62   ASSERT_TRUE(sampler->HasUnrecordedEvent());
63   sampler->RecordSample();
64   ASSERT_FALSE(sampler->HasUnrecordedEvent());
65
66   // After more than 250 ms has passed without considering an event, we should
67   // repeatedly be overdue for sampling.  However, once the redundant capture
68   // goal is achieved, we should no longer be overdue for sampling.
69   *t += base::TimeDelta::FromMilliseconds(250);
70   for (int i = 0; i < redundant_capture_goal; i++) {
71     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
72     ASSERT_FALSE(sampler->HasUnrecordedEvent());
73     ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t))
74         << "Should sample until redundant capture goal is hit";
75     sampler->RecordSample();
76     *t += capture_period;  // Timer fires once every capture period.
77   }
78   ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t))
79       << "Should not be overdue once redundant capture goal achieved.";
80 }
81
82 }  // namespace
83
84 // 60Hz sampled at 30Hz should produce 30Hz.  In addition, this test contains
85 // much more comprehensive before/after/edge-case scenarios than the others.
86 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) {
87   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
88   const int redundant_capture_goal = 200;
89   const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60;
90
91   SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
92   base::TimeTicks t = InitialTestTimeTicks();
93
94   TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
95                                &sampler, &t);
96
97   // Steady state, we should capture every other vsync, indefinitely.
98   for (int i = 0; i < 100; i++) {
99     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
100     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
101     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
102   }
103
104   // Now pretend we're limited by backpressure in the pipeline. In this scenario
105   // case we are adding events but not sampling them.
106   for (int i = 0; i < 20; i++) {
107     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
108     ASSERT_EQ(i >= 14, sampler.IsOverdueForSamplingAt(t));
109     ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
110     ASSERT_TRUE(sampler.HasUnrecordedEvent());
111     t += vsync;
112   }
113
114   // Now suppose we can sample again. We should be back in the steady state,
115   // but at a different phase.
116   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
117   for (int i = 0; i < 100; i++) {
118     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
119     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
120     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
121   }
122 }
123
124 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
125 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) {
126   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
127   const int redundant_capture_goal = 2;
128   const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50;
129
130   SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
131   base::TimeTicks t = InitialTestTimeTicks();
132
133   TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
134                                &sampler, &t);
135
136   // Steady state, we should capture 1st, 2nd and 4th frames out of every five
137   // frames, indefinitely.
138   for (int i = 0; i < 100; i++) {
139     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
140     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
141     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
142     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
143     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
144     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
145   }
146
147   // Now pretend we're limited by backpressure in the pipeline. In this scenario
148   // case we are adding events but not sampling them.
149   for (int i = 0; i < 20; i++) {
150     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
151     ASSERT_EQ(i >= 11, sampler.IsOverdueForSamplingAt(t));
152     ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
153     t += vsync;
154   }
155
156   // Now suppose we can sample again. We should be back in the steady state
157   // again.
158   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
159   for (int i = 0; i < 100; i++) {
160     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
161     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
162     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
163     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
164     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
165     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
166   }
167 }
168
169 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
170 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) {
171   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
172   const int redundant_capture_goal = 32;
173   const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75;
174
175   SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
176   base::TimeTicks t = InitialTestTimeTicks();
177
178   TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
179                                &sampler, &t);
180
181   // Steady state, we should capture 1st and 3rd frames out of every five
182   // frames, indefinitely.
183   SteadyStateSampleAndAdvance(vsync, &sampler, &t);
184   SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
185   for (int i = 0; i < 100; i++) {
186     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
187     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
188     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
189     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
190     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
191     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
192   }
193
194   // Now pretend we're limited by backpressure in the pipeline. In this scenario
195   // case we are adding events but not sampling them.
196   for (int i = 0; i < 20; i++) {
197     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
198     ASSERT_EQ(i >= 16, sampler.IsOverdueForSamplingAt(t));
199     ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
200     t += vsync;
201   }
202
203   // Now suppose we can sample again. We capture the next frame, and not the one
204   // after that, and then we're back in the steady state again.
205   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
206   SteadyStateSampleAndAdvance(vsync, &sampler, &t);
207   SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
208   for (int i = 0; i < 100; i++) {
209     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
210     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
211     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
212     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
213     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
214     SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
215   }
216 }
217
218 // 30Hz sampled at 30Hz should produce 30Hz.
219 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) {
220   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
221   const int redundant_capture_goal = 1;
222   const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30;
223
224   SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
225   base::TimeTicks t = InitialTestTimeTicks();
226
227   TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
228                                &sampler, &t);
229
230   // Steady state, we should capture every vsync, indefinitely.
231   for (int i = 0; i < 200; i++) {
232     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
233     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
234   }
235
236   // Now pretend we're limited by backpressure in the pipeline. In this scenario
237   // case we are adding events but not sampling them.
238   for (int i = 0; i < 10; i++) {
239     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
240     ASSERT_EQ(i >= 7, sampler.IsOverdueForSamplingAt(t));
241     ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
242     t += vsync;
243   }
244
245   // Now suppose we can sample again. We should be back in the steady state.
246   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
247   for (int i = 0; i < 100; i++) {
248     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
249     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
250   }
251 }
252
253 // 24Hz sampled at 30Hz should produce 24Hz.
254 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) {
255   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
256   const int redundant_capture_goal = 333;
257   const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24;
258
259   SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
260   base::TimeTicks t = InitialTestTimeTicks();
261
262   TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
263                                &sampler, &t);
264
265   // Steady state, we should capture every vsync, indefinitely.
266   for (int i = 0; i < 200; i++) {
267     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
268     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
269   }
270
271   // Now pretend we're limited by backpressure in the pipeline. In this scenario
272   // case we are adding events but not sampling them.
273   for (int i = 0; i < 10; i++) {
274     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
275     ASSERT_EQ(i >= 6, sampler.IsOverdueForSamplingAt(t));
276     ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
277     t += vsync;
278   }
279
280   // Now suppose we can sample again. We should be back in the steady state.
281   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
282   for (int i = 0; i < 100; i++) {
283     SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
284     SteadyStateSampleAndAdvance(vsync, &sampler, &t);
285   }
286 }
287
288 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) {
289   const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
290   const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1);
291
292   SmoothEventSampler sampler(capture_period, true, 1);
293   base::TimeTicks t = InitialTestTimeTicks();
294
295   ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
296   sampler.RecordSample();
297   ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t))
298       << "Sampled last event; should not be dirty.";
299   t += overdue_period;
300
301   // Now simulate 2 events with the same clock value.
302   ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
303   sampler.RecordSample();
304   ASSERT_FALSE(AddEventAndConsiderSampling(&sampler, t))
305       << "Two events at same time -- expected second not to be sampled.";
306   ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period))
307       << "Second event should dirty the capture state.";
308   sampler.RecordSample();
309   ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period));
310 }
311
312 TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) {
313   const base::TimeDelta timer_interval = base::TimeDelta::FromSeconds(1) / 30;
314
315   SmoothEventSampler should_not_poll(timer_interval, true, 1);
316   SmoothEventSampler should_poll(timer_interval, false, 1);
317   base::TimeTicks t = InitialTestTimeTicks();
318
319   // Do one round of the "happy case" where an event was received and
320   // RecordSample() was called by the client.
321   ASSERT_TRUE(AddEventAndConsiderSampling(&should_not_poll, t));
322   ASSERT_TRUE(AddEventAndConsiderSampling(&should_poll, t));
323   should_not_poll.RecordSample();
324   should_poll.RecordSample();
325
326   // For the following time period, before 250 ms has elapsed, neither sampler
327   // says we're overdue.
328   const int non_overdue_intervals = static_cast<int>(
329       base::TimeDelta::FromMilliseconds(250) / timer_interval);
330   for (int i = 0; i < non_overdue_intervals; i++) {
331     t += timer_interval;
332     ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
333         << "Sampled last event; should not be dirty.";
334     ASSERT_FALSE(should_poll.IsOverdueForSamplingAt(t))
335         << "Dirty interval has not elapsed yet.";
336   }
337
338   // Next time period ahead, both samplers say we're overdue.  The non-polling
339   // sampler is returning true here because it has been configured to allow one
340   // redundant capture.
341   t += timer_interval;  // Step past the 250 ms threshold.
342   ASSERT_TRUE(should_not_poll.IsOverdueForSamplingAt(t))
343       << "Sampled last event; is dirty one time only to meet redundancy goal.";
344   ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
345       << "If updates are unreliable, must fall back to polling when idle.";
346   should_not_poll.RecordSample();
347   should_poll.RecordSample();
348
349   // Forever more, the non-polling sampler returns false while the polling one
350   // returns true.
351   for (int i = 0; i < 100; ++i) {
352     t += timer_interval;
353     ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
354         << "Sampled last event; should not be dirty.";
355     ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
356         << "If updates are unreliable, must fall back to polling when idle.";
357     should_poll.RecordSample();
358   }
359   t += timer_interval / 3;
360   ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
361       << "Sampled last event; should not be dirty.";
362   ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
363       << "If updates are unreliable, must fall back to polling when idle.";
364   should_poll.RecordSample();
365 }
366
367 namespace {
368
369 struct DataPoint {
370   bool should_capture;
371   double increment_ms;
372 };
373
374 void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
375                                     size_t num_data_points,
376                                     SmoothEventSampler* sampler) {
377   base::TimeTicks t = InitialTestTimeTicks();
378   for (size_t i = 0; i < num_data_points; ++i) {
379     t += base::TimeDelta::FromMicroseconds(
380         static_cast<int64>(data_points[i].increment_ms * 1000));
381     ASSERT_EQ(data_points[i].should_capture,
382               AddEventAndConsiderSampling(sampler, t))
383         << "at data_points[" << i << ']';
384     if (data_points[i].should_capture)
385       sampler->RecordSample();
386   }
387 }
388
389 }  // namespace
390
391 TEST(SmoothEventSamplerTest, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz) {
392   // Actual capturing of timing data: Initial instability as a 24 FPS video was
393   // started from a still screen, then clearly followed by steady-state.
394   static const DataPoint data_points[] = {
395     { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
396     { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
397     { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
398     { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
399     { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
400     { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
401     { false, 0 }, { true, 50.161 }, { false, 0 }, { true, 33.44 },
402     { true, 16.72 }, { false, 16.721 }, { true, 66.881 }, { false, 0 },
403     { true, 33.441 }, { true, 16.72 }, { true, 50.16 }, { true, 16.72 },
404     { false, 16.721 }, { true, 50.161 }, { true, 50.16 }, { false, 0 },
405     { true, 33.441 }, { true, 50.337 }, { true, 50.183 }, { true, 16.722 },
406     { true, 50.161 }, { true, 33.441 }, { true, 50.16 }, { true, 33.441 },
407     { true, 50.16 }, { true, 33.441 }, { true, 50.16 }, { true, 33.44 },
408     { true, 50.161 }, { true, 50.16 }, { true, 33.44 }, { true, 33.441 },
409     { true, 50.16 }, { true, 50.161 }, { true, 33.44 }, { true, 33.441 },
410     { true, 50.16 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
411     { true, 50.161 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
412     { true, 83.601 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }
413   };
414
415   SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
416   ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
417 }
418
419 TEST(SmoothEventSamplerTest, DrawingAt30FpsWith60HzVsyncSampledAt30Hertz) {
420   // Actual capturing of timing data: Initial instability as a 30 FPS video was
421   // started from a still screen, then followed by steady-state.  Drawing
422   // framerate from the video rendering was a bit volatile, but averaged 30 FPS.
423   static const DataPoint data_points[] = {
424     { true, 2407.69 }, { true, 16.733 }, { true, 217.362 }, { true, 33.441 },
425     { true, 33.44 }, { true, 33.44 }, { true, 33.441 }, { true, 33.44 },
426     { true, 33.44 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 },
427     { true, 16.721 }, { true, 33.44 }, { false, 0 }, { true, 50.161 },
428     { true, 50.16 }, { false, 0 }, { true, 50.161 }, { true, 33.44 },
429     { true, 16.72 }, { false, 0 }, { false, 16.72 }, { true, 66.881 },
430     { false, 0 }, { true, 33.44 }, { true, 16.72 }, { true, 50.161 },
431     { false, 0 }, { true, 33.538 }, { true, 33.526 }, { true, 33.447 },
432     { true, 33.445 }, { true, 33.441 }, { true, 16.721 }, { true, 33.44 },
433     { true, 33.44 }, { true, 50.161 }, { true, 16.72 }, { true, 33.44 },
434     { true, 33.441 }, { true, 33.44 }, { false, 0 }, { false, 16.72 },
435     { true, 66.881 }, { true, 16.72 }, { false, 16.72 }, { true, 50.16 },
436     { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 33.44 },
437     { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { false, 0 },
438     { true, 33.44 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
439     { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 66.88 },
440     { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
441     { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
442     { true, 16.72 }, { true, 50.161 }, { false, 0 }, { true, 50.16 },
443     { false, 0.001 }, { true, 16.721 }, { true, 66.88 }, { true, 33.44 },
444     { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
445     { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 66.881 },
446     { true, 33.44 }, { true, 16.72 }, { true, 33.441 }, { false, 16.72 },
447     { true, 66.88 }, { true, 16.721 }, { true, 50.16 }, { true, 33.44 },
448     { true, 16.72 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 }
449   };
450
451   SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
452   ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
453 }
454
455 TEST(SmoothEventSamplerTest, DrawingAt60FpsWith60HzVsyncSampledAt30Hertz) {
456   // Actual capturing of timing data: WebGL Acquarium demo
457   // (http://webglsamples.googlecode.com/hg/aquarium/aquarium.html) which ran
458   // between 55-60 FPS in the steady-state.
459   static const DataPoint data_points[] = {
460     { true, 16.72 }, { true, 16.72 }, { true, 4163.29 }, { true, 50.193 },
461     { true, 117.041 }, { true, 50.161 }, { true, 50.16 }, { true, 33.441 },
462     { true, 50.16 }, { true, 33.44 }, { false, 0 }, { false, 0 },
463     { true, 50.161 }, { true, 83.601 }, { true, 50.16 }, { true, 16.72 },
464     { true, 33.441 }, { false, 16.72 }, { true, 50.16 }, { true, 16.72 },
465     { false, 0.001 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
466     { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
467     { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
468     { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
469     { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
470     { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
471     { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 33.44 },
472     { false, 0 }, { true, 16.721 }, { true, 50.161 }, { false, 0 },
473     { true, 33.44 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
474     { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
475     { true, 50.16 }, { false, 0 }, { true, 16.721 }, { true, 33.44 },
476     { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
477     { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
478     { false, 0 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
479     { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
480     { true, 33.44 }, { false, 0 }, { true, 33.44 }, { true, 33.441 },
481     { false, 0 }, { true, 33.44 }, { true, 33.441 }, { false, 0 },
482     { true, 33.44 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
483     { true, 16.721 }, { true, 50.161 }, { false, 0 }, { true, 16.72 },
484     { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 33.44 },
485     { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
486     { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
487     { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
488     { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
489     { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
490     { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
491   };
492
493   SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
494   ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
495 }
496
497 class AnimatedContentSamplerTest : public ::testing::Test {
498  public:
499   AnimatedContentSamplerTest() {}
500   virtual ~AnimatedContentSamplerTest() {}
501
502   virtual void SetUp() OVERRIDE {
503     const base::TimeDelta since_epoch =
504         InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
505     rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds()));
506     sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod()));
507   }
508
509  protected:
510   // Overridden by subclass for parameterized tests.
511   virtual base::TimeDelta GetMinCapturePeriod() const {
512     return base::TimeDelta::FromSeconds(1) / 30;
513   }
514
515   AnimatedContentSampler* sampler() const {
516     return sampler_.get();
517   }
518
519   int GetRandomInRange(int begin, int end) {
520     const int len = end - begin;
521     const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
522     return begin + rand_offset;
523   }
524
525   gfx::Rect GetRandomDamageRect() {
526     return gfx::Rect(0, 0, GetRandomInRange(1, 100), GetRandomInRange(1, 100));
527   }
528
529   gfx::Rect GetContentDamageRect() {
530     // This must be distinct from anything GetRandomDamageRect() could return.
531     return gfx::Rect(0, 0, 1280, 720);
532   }
533
534   // Directly inject an observation.  Only used to test
535   // ElectMajorityDamageRect().
536   void ObserveDamageRect(const gfx::Rect& damage_rect) {
537     sampler_->observations_.push_back(
538         AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
539   }
540
541   gfx::Rect ElectMajorityDamageRect() const {
542     return sampler_->ElectMajorityDamageRect();
543   }
544
545  private:
546   // Note: Not using base::RandInt() because it is horribly slow on debug
547   // builds.  The following is a very simple, deterministic LCG:
548   int NextRandomInt() {
549     rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
550     return rand_seed_;
551   }
552
553   int rand_seed_;
554   scoped_ptr<AnimatedContentSampler> sampler_;
555 };
556
557 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromZeroDamageRects) {
558   EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
559 }
560
561 TEST_F(AnimatedContentSamplerTest, ElectsMajorityFromOneDamageRect) {
562   const gfx::Rect the_one_rect(0, 0, 1, 1);
563   ObserveDamageRect(the_one_rect);
564   EXPECT_EQ(the_one_rect, ElectMajorityDamageRect());
565 }
566
567 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromTwoDamageRectsOfSameArea) {
568   const gfx::Rect one_rect(0, 0, 1, 1);
569   const gfx::Rect another_rect(1, 1, 1, 1);
570   ObserveDamageRect(one_rect);
571   ObserveDamageRect(another_rect);
572   EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
573 }
574
575 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_1) {
576   const gfx::Rect one_rect(0, 0, 1, 1);
577   const gfx::Rect another_rect(0, 0, 2, 2);
578   ObserveDamageRect(one_rect);
579   ObserveDamageRect(another_rect);
580   EXPECT_EQ(another_rect, ElectMajorityDamageRect());
581 }
582
583 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_2) {
584   const gfx::Rect one_rect(0, 0, 2, 2);
585   const gfx::Rect another_rect(0, 0, 1, 1);
586   ObserveDamageRect(one_rect);
587   ObserveDamageRect(another_rect);
588   EXPECT_EQ(one_rect, ElectMajorityDamageRect());
589 }
590
591 TEST_F(AnimatedContentSamplerTest, ElectsSameAsMooreDemonstration) {
592   // A more complex sequence (from Moore's web site): Three different Rects with
593   // the same area, but occurring a different number of times.  C should win the
594   // vote.
595   const gfx::Rect rect_a(0, 0, 1, 4);
596   const gfx::Rect rect_b(1, 1, 4, 1);
597   const gfx::Rect rect_c(2, 2, 2, 2);
598   for (int i = 0; i < 3; ++i)
599     ObserveDamageRect(rect_a);
600   for (int i = 0; i < 2; ++i)
601     ObserveDamageRect(rect_c);
602   for (int i = 0; i < 2; ++i)
603     ObserveDamageRect(rect_b);
604   for (int i = 0; i < 3; ++i)
605     ObserveDamageRect(rect_c);
606   ObserveDamageRect(rect_b);
607   for (int i = 0; i < 2; ++i)
608     ObserveDamageRect(rect_c);
609   EXPECT_EQ(rect_c, ElectMajorityDamageRect());
610 }
611
612 TEST_F(AnimatedContentSamplerTest, Elects24FpsVideoInsteadOf48FpsSpinner) {
613   // Scenario: 24 FPS 720x480 Video versus 48 FPS 96x96 "Busy Spinner"
614   const gfx::Rect video_rect(100, 100, 720, 480);
615   const gfx::Rect spinner_rect(360, 0, 96, 96);
616   for (int i = 0; i < 100; ++i) {
617     // |video_rect| occurs once for every two |spinner_rect|.  Vary the order
618     // of events between the two:
619     ObserveDamageRect(video_rect);
620     ObserveDamageRect(spinner_rect);
621     ObserveDamageRect(spinner_rect);
622     ObserveDamageRect(video_rect);
623     ObserveDamageRect(spinner_rect);
624     ObserveDamageRect(spinner_rect);
625     ObserveDamageRect(spinner_rect);
626     ObserveDamageRect(video_rect);
627     ObserveDamageRect(spinner_rect);
628     ObserveDamageRect(spinner_rect);
629     ObserveDamageRect(video_rect);
630     ObserveDamageRect(spinner_rect);
631   }
632   EXPECT_EQ(video_rect, ElectMajorityDamageRect());
633 }
634
635 namespace {
636
637 // A test scenario for AnimatedContentSamplerParameterizedTest.
638 struct Scenario {
639   base::TimeDelta vsync_interval;  // Reflects compositor's update rate.
640   base::TimeDelta min_capture_period;  // Reflects maximum capture rate.
641   base::TimeDelta content_period;  // Reflects content animation rate.
642
643   Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c)
644       : vsync_interval(v), min_capture_period(m), content_period(c) {
645     CHECK(content_period >= vsync_interval)
646         << "Bad test params: Impossible to animate faster than the compositor.";
647   }
648 };
649
650 // Value printer for Scenario.
651 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
652   return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
653             << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
654             << ", content_period=" << s.content_period.InMicroseconds()
655             << " }";
656 }
657
658 base::TimeDelta FpsAsPeriod(int frame_rate) {
659   return base::TimeDelta::FromSeconds(1) / frame_rate;
660 }
661
662 }  // namespace
663
664 class AnimatedContentSamplerParameterizedTest
665     : public AnimatedContentSamplerTest,
666       public ::testing::WithParamInterface<Scenario> {
667  public:
668   AnimatedContentSamplerParameterizedTest()
669       : count_dropped_frames_(0), count_sampled_frames_(0) {}
670   virtual ~AnimatedContentSamplerParameterizedTest() {}
671
672  protected:
673   typedef std::pair<gfx::Rect, base::TimeTicks> Event;
674
675   virtual base::TimeDelta GetMinCapturePeriod() const OVERRIDE {
676     return GetParam().min_capture_period;
677   }
678
679   // Generate a sequence of events from the compositor pipeline.  The event
680   // times will all be at compositor vsync boundaries.
681   std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
682                                            base::TimeTicks end,
683                                            bool include_content_frame_events,
684                                            bool include_random_events) {
685     DCHECK(GetParam().content_period >= GetParam().vsync_interval);
686     base::TimeTicks next_content_time = begin - GetParam().content_period;
687     std::vector<Event> events;
688     for (base::TimeTicks compositor_time = begin; compositor_time < end;
689          compositor_time += GetParam().vsync_interval) {
690       if (include_content_frame_events && next_content_time < compositor_time) {
691         events.push_back(Event(GetContentDamageRect(), compositor_time));
692         next_content_time += GetParam().content_period;
693       } else if (include_random_events && GetRandomInRange(0, 1) == 0) {
694         events.push_back(Event(GetRandomDamageRect(), compositor_time));
695       }
696     }
697
698     DCHECK(!events.empty());
699     return events;
700   }
701
702   // Feed |events| through the sampler, and detect whether the expected
703   // lock-in/out transition occurs.  Also, track and measure the frame drop
704   // ratio and check it against the expected drop rate.
705   void RunEventSequence(const std::vector<Event> events,
706                         bool was_detecting_before,
707                         bool is_detecting_after,
708                         bool simulate_pipeline_back_pressure) {
709     gfx::Rect first_detected_region;
710
711     EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
712     bool has_detection_switched = false;
713     ResetFrameCounters();
714     for (std::vector<Event>::const_iterator i = events.begin();
715          i != events.end(); ++i) {
716       sampler()->ConsiderPresentationEvent(i->first, i->second);
717
718       // Detect when the sampler locks in/out, and that it stays that way for
719       // all further iterations of this loop.
720       if (!has_detection_switched &&
721           was_detecting_before != sampler()->HasProposal()) {
722         has_detection_switched = true;
723       }
724       ASSERT_EQ(
725           has_detection_switched ? is_detecting_after : was_detecting_before,
726           sampler()->HasProposal());
727
728       if (sampler()->HasProposal()) {
729         // Make sure the sampler doesn't flip-flop and keep proposing sampling
730         // based on locking into different regions.
731         if (first_detected_region.IsEmpty()) {
732           first_detected_region = sampler()->detected_region();
733           ASSERT_FALSE(first_detected_region.IsEmpty());
734         } else {
735           EXPECT_EQ(first_detected_region, sampler()->detected_region());
736         }
737
738         if (simulate_pipeline_back_pressure && GetRandomInRange(0, 2) == 0)
739           ClientCannotSampleFrame(*i);
740         else
741           ClientDoesWhatSamplerProposes(*i);
742       } else {
743         EXPECT_FALSE(sampler()->ShouldSample());
744         if (!simulate_pipeline_back_pressure || GetRandomInRange(0, 2) == 1)
745           sampler()->RecordSample(i->second);
746       }
747     }
748     EXPECT_EQ(is_detecting_after, sampler()->HasProposal());
749     ExpectFrameDropRatioIsCorrect();
750   }
751
752   void ResetFrameCounters() {
753     count_dropped_frames_ = 0;
754     count_sampled_frames_ = 0;
755   }
756
757   // Keep track what the sampler is proposing, and call RecordSample() if it
758   // proposes sampling |event|.
759   void ClientDoesWhatSamplerProposes(const Event& event) {
760     if (sampler()->ShouldSample()) {
761       EXPECT_EQ(GetContentDamageRect(), event.first);
762       sampler()->RecordSample(sampler()->frame_timestamp());
763       ++count_sampled_frames_;
764     } else if (event.first == GetContentDamageRect()) {
765       ++count_dropped_frames_;
766     }
767   }
768
769   // RecordSample() is not called, but for testing, keep track of what the
770   // sampler is proposing for |event|.
771   void ClientCannotSampleFrame(const Event& event) {
772     if (sampler()->ShouldSample()) {
773       EXPECT_EQ(GetContentDamageRect(), event.first);
774       ++count_sampled_frames_;
775     } else if (event.first == GetContentDamageRect()) {
776       ++count_dropped_frames_;
777     }
778   }
779
780   // Confirm the AnimatedContentSampler is not dropping more frames than
781   // expected, given current test parameters.
782   void ExpectFrameDropRatioIsCorrect() {
783     if (count_sampled_frames_ == 0) {
784       EXPECT_EQ(0, count_dropped_frames_);
785       return;
786     }
787     const double content_framerate =
788         1000000.0 / GetParam().content_period.InMicroseconds();
789     const double capture_framerate =
790         1000000.0 / GetParam().min_capture_period.InMicroseconds();
791     const double expected_drop_rate = std::max(
792         0.0, (content_framerate - capture_framerate) / capture_framerate);
793     const double actual_drop_rate =
794         static_cast<double>(count_dropped_frames_) / count_sampled_frames_;
795     EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015);
796   }
797
798  private:
799   // These counters only include the frames with the desired content.
800   int count_dropped_frames_;
801   int count_sampled_frames_;
802 };
803
804 // Tests that the implementation locks in/out of frames containing stable
805 // animated content, whether or not random events are also simultaneously
806 // present.
807 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
808   // |begin| refers to the start of an event sequence in terms of the
809   // Compositor's clock.
810   base::TimeTicks begin = InitialTestTimeTicks();
811
812   // Provide random events and expect no lock-in.
813   base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
814   RunEventSequence(GenerateEventSequence(begin, end, false, true),
815                    false,
816                    false,
817                    false);
818   begin = end;
819
820   // Provide content frame events with some random events mixed-in, and expect
821   // the sampler to lock-in.
822   end = begin + base::TimeDelta::FromSeconds(5);
823   RunEventSequence(GenerateEventSequence(begin, end, true, true),
824                    false,
825                    true,
826                    false);
827   begin = end;
828
829   // Continue providing content frame events without the random events mixed-in
830   // and expect the lock-in to hold.
831   end = begin + base::TimeDelta::FromSeconds(5);
832   RunEventSequence(GenerateEventSequence(begin, end, true, false),
833                    true,
834                    true,
835                    false);
836   begin = end;
837
838   // Continue providing just content frame events and expect the lock-in to
839   // hold.  Also simulate the capture pipeline experiencing back pressure.
840   end = begin + base::TimeDelta::FromSeconds(20);
841   RunEventSequence(GenerateEventSequence(begin, end, true, false),
842                    true,
843                    true,
844                    true);
845   begin = end;
846
847   // Provide a half-second of random events only, and expect the lock-in to be
848   // broken.
849   end = begin + base::TimeDelta::FromMilliseconds(500);
850   RunEventSequence(GenerateEventSequence(begin, end, false, true),
851                    true,
852                    false,
853                    false);
854   begin = end;
855
856   // Now, go back to providing content frame events, and expect the sampler to
857   // lock-in once again.
858   end = begin + base::TimeDelta::FromSeconds(5);
859   RunEventSequence(GenerateEventSequence(begin, end, true, false),
860                    false,
861                    true,
862                    false);
863   begin = end;
864 }
865
866 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
867 // two animations of the same pixel change rate.  VideoCaptureOracle should
868 // revert to using the SmoothEventSampler for these kinds of situations, as
869 // there is no "right answer" as to which animation to lock into.
870 TEST_P(AnimatedContentSamplerParameterizedTest,
871        DoesNotLockInToTwoCompetingAnimations) {
872   // Don't test when the event stream cannot indicate two separate content
873   // animations under the current test parameters.
874   if (GetParam().content_period < 2 * GetParam().vsync_interval)
875     return;
876
877   // Start the first animation and run for a bit, and expect the sampler to
878   // lock-in.
879   base::TimeTicks begin = InitialTestTimeTicks();
880   base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
881   RunEventSequence(GenerateEventSequence(begin, end, true, false),
882                    false,
883                    true,
884                    false);
885   begin = end;
886
887   // Now, keep the first animation and blend in an second animation of the same
888   // size and frame rate, but at a different position.  This will should cause
889   // the sampler to enter an "undetected" state since it's unclear which
890   // animation should be locked into.
891   end = begin + base::TimeDelta::FromSeconds(20);
892   std::vector<Event> first_animation_events =
893       GenerateEventSequence(begin, end, true, false);
894   gfx::Rect second_animation_rect(
895       gfx::Point(0, GetContentDamageRect().height()),
896       GetContentDamageRect().size());
897   std::vector<Event> both_animations_events;
898   base::TimeDelta second_animation_offset = GetParam().vsync_interval;
899   for (std::vector<Event>::const_iterator i = first_animation_events.begin();
900        i != first_animation_events.end(); ++i) {
901     both_animations_events.push_back(*i);
902     both_animations_events.push_back(
903         Event(second_animation_rect, i->second + second_animation_offset));
904   }
905   RunEventSequence(both_animations_events, true, false, false);
906   begin = end;
907
908   // Now, run just the first animation, and expect the sampler to lock-in once
909   // again.
910   end = begin + base::TimeDelta::FromSeconds(5);
911   RunEventSequence(GenerateEventSequence(begin, end, true, false),
912                    false,
913                    true,
914                    false);
915   begin = end;
916
917   // Now, blend in the second animation again, but it has half the frame rate of
918   // the first animation and damage Rects with twice the area.  This will should
919   // cause the sampler to enter an "undetected" state again.  This tests that
920   // pixel-weighting is being accounted for in the sampler's logic.
921   end = begin + base::TimeDelta::FromSeconds(20);
922   first_animation_events = GenerateEventSequence(begin, end, true, false);
923   second_animation_rect.set_width(second_animation_rect.width() * 2);
924   both_animations_events.clear();
925   bool include_second_animation_frame = true;
926   for (std::vector<Event>::const_iterator i = first_animation_events.begin();
927        i != first_animation_events.end(); ++i) {
928     both_animations_events.push_back(*i);
929     if (include_second_animation_frame) {
930       both_animations_events.push_back(
931           Event(second_animation_rect, i->second + second_animation_offset));
932     }
933     include_second_animation_frame = !include_second_animation_frame;
934   }
935   RunEventSequence(both_animations_events, true, false, false);
936   begin = end;
937 }
938
939 // Tests that the frame timestamps are smooth; meaning, that when run through a
940 // simulated compositor, each frame is held displayed for the right number of
941 // v-sync intervals.
942 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
943   // Generate 30 seconds of animated content events, run the events through
944   // AnimatedContentSampler, and record all frame timestamps being proposed
945   // once lock-in is continuous.
946   base::TimeTicks begin = InitialTestTimeTicks();
947   std::vector<Event> events = GenerateEventSequence(
948       begin,
949       begin + base::TimeDelta::FromSeconds(20),
950       true,
951       false);
952   typedef std::vector<base::TimeTicks> Timestamps;
953   Timestamps frame_timestamps;
954   for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
955        ++i) {
956     sampler()->ConsiderPresentationEvent(i->first, i->second);
957     if (sampler()->HasProposal()) {
958       if (sampler()->ShouldSample()) {
959         frame_timestamps.push_back(sampler()->frame_timestamp());
960         sampler()->RecordSample(sampler()->frame_timestamp());
961       }
962     } else {
963       frame_timestamps.clear();  // Reset until continuous lock-in.
964     }
965   }
966   ASSERT_LE(2u, frame_timestamps.size());
967
968   // Iterate through the |frame_timestamps|, building a histogram counting the
969   // number of times each frame was displayed k times.  For example, 10 frames
970   // of 30 Hz content on a 60 Hz v-sync interval should result in
971   // display_counts[2] == 10.  Quit early if any one frame was obviously
972   // repeated too many times.
973   const int64 max_expected_repeats_per_frame = 1 +
974       std::max(GetParam().min_capture_period, GetParam().content_period) /
975           GetParam().vsync_interval;
976   std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
977   base::TimeTicks last_present_time = frame_timestamps.front();
978   for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
979        i != frame_timestamps.end(); ++i) {
980     const size_t num_vsync_intervals = static_cast<size_t>(
981         (*i - last_present_time) / GetParam().vsync_interval);
982     ASSERT_LT(0u, num_vsync_intervals);
983     ASSERT_GT(display_counts.size(), num_vsync_intervals);  // Quit early.
984     ++display_counts[num_vsync_intervals];
985     last_present_time += num_vsync_intervals * GetParam().vsync_interval;
986   }
987
988   // Analyze the histogram for an expected result pattern.  If the frame
989   // timestamps are smooth, there should only be one or two buckets with
990   // non-zero counts and they should be next to each other.  Because the clock
991   // precision for the event_times provided to the sampler is very granular
992   // (i.e., the vsync_interval), it's okay if other buckets have a tiny "stray"
993   // count in this test.
994   size_t highest_count = 0;
995   size_t second_highest_count = 0;
996   for (size_t repeats = 1; repeats < display_counts.size(); ++repeats) {
997     DVLOG(1) << "display_counts[" << repeats << "] is "
998              << display_counts[repeats];
999     if (display_counts[repeats] >= highest_count) {
1000       second_highest_count = highest_count;
1001       highest_count = display_counts[repeats];
1002     } else if (display_counts[repeats] > second_highest_count) {
1003       second_highest_count = display_counts[repeats];
1004     }
1005   }
1006   size_t stray_count_remaining =
1007       (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
1008   // Expect no more than 0.75% of frames fall outside the two main buckets.
1009   EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
1010   for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
1011     if (display_counts[repeats] == highest_count) {
1012       EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
1013       ++repeats;
1014     } else if (display_counts[repeats] == second_highest_count) {
1015       EXPECT_EQ(highest_count, display_counts[repeats + 1]);
1016       ++repeats;
1017     } else {
1018       EXPECT_GE(stray_count_remaining, display_counts[repeats]);
1019       stray_count_remaining -= display_counts[repeats];
1020     }
1021   }
1022 }
1023
1024 // Tests that frame timestamps are "lightly pushed" back towards the original
1025 // presentation event times, which tells us the AnimatedContentSampler can
1026 // account for sources of timestamp drift and correct the drift.
1027 TEST_P(AnimatedContentSamplerParameterizedTest,
1028        FrameTimestampsConvergeTowardsEventTimes) {
1029   const int max_drift_increment_millis = 3;
1030
1031   // Generate a full minute of events.
1032   const base::TimeTicks begin = InitialTestTimeTicks();
1033   const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1);
1034   std::vector<Event> events = GenerateEventSequence(begin, end, true, false);
1035
1036   // Modify the event sequence so that 1-3 ms of additional drift is suddenly
1037   // present every 100 events.  This is meant to simulate that, external to
1038   // AnimatedContentSampler, the video hardware vsync timebase is being
1039   // refreshed and is showing severe drift from the system clock.
1040   base::TimeDelta accumulated_drift;
1041   for (size_t i = 1; i < events.size(); ++i) {
1042     if (i % 100 == 0) {
1043       accumulated_drift += base::TimeDelta::FromMilliseconds(
1044           GetRandomInRange(1, max_drift_increment_millis + 1));
1045     }
1046     events[i].second += accumulated_drift;
1047   }
1048
1049   // Run all the events through the sampler and track the last rewritten frame
1050   // timestamp.
1051   base::TimeTicks last_frame_timestamp;
1052   for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
1053        ++i) {
1054     sampler()->ConsiderPresentationEvent(i->first, i->second);
1055     if (sampler()->ShouldSample())
1056       last_frame_timestamp = sampler()->frame_timestamp();
1057   }
1058
1059   // If drift was accounted for, the |last_frame_timestamp| should be close to
1060   // the last event's timestamp.
1061   const base::TimeDelta total_error =
1062       events.back().second - last_frame_timestamp;
1063   const base::TimeDelta max_acceptable_error = GetParam().min_capture_period +
1064       base::TimeDelta::FromMilliseconds(max_drift_increment_millis);
1065   EXPECT_NEAR(0.0,
1066               total_error.InMicroseconds(),
1067               max_acceptable_error.InMicroseconds());
1068 }
1069
1070 INSTANTIATE_TEST_CASE_P(
1071     ,
1072     AnimatedContentSamplerParameterizedTest,
1073     ::testing::Values(
1074          // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
1075          // Hz, and content video animates at 30, 25, or 24 Hz.
1076          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)),
1077          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)),
1078          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)),
1079
1080          // High frame rate content that leverages the Compositor's
1081          // capabilities, but capture is still at 30 Hz.
1082          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)),
1083          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)),
1084          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)),
1085
1086          // High frame rate content that leverages the Compositor's
1087          // capabilities, and capture is also a buttery 60 Hz.
1088          Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)),
1089          Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)),
1090          Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)),
1091
1092          // On some platforms, the Compositor runs at 50 Hz.
1093          Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)),
1094          Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)),
1095          Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)),
1096          Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)),
1097          Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)),
1098
1099          // Stable, but non-standard content frame rates.
1100          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)),
1101          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)),
1102          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)),
1103          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)),
1104          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)),
1105          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)),
1106          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)),
1107          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)),
1108          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)),
1109          Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33))));
1110
1111 // Tests that VideoCaptureOracle filters out events whose timestamps are
1112 // decreasing.
1113 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) {
1114   const base::TimeDelta min_capture_period =
1115       base::TimeDelta::FromSeconds(1) / 30;
1116   const gfx::Rect damage_rect(0, 0, 1280, 720);
1117   const base::TimeDelta event_increment = min_capture_period * 2;
1118
1119   VideoCaptureOracle oracle(min_capture_period, true);
1120
1121   base::TimeTicks t = InitialTestTimeTicks();
1122   for (int i = 0; i < 10; ++i) {
1123     t += event_increment;
1124     ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1125         VideoCaptureOracle::kCompositorUpdate,
1126         damage_rect, t));
1127   }
1128
1129   base::TimeTicks furthest_event_time = t;
1130   for (int i = 0; i < 10; ++i) {
1131     t -= event_increment;
1132     ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
1133         VideoCaptureOracle::kCompositorUpdate,
1134         damage_rect, t));
1135   }
1136
1137   t = furthest_event_time;
1138   for (int i = 0; i < 10; ++i) {
1139     t += event_increment;
1140     ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1141         VideoCaptureOracle::kCompositorUpdate,
1142         damage_rect, t));
1143   }
1144 }
1145
1146 // Tests that VideoCaptureOracle is enforcing the requirement that captured
1147 // frames are delivered in order.  Otherwise, downstream consumers could be
1148 // tripped-up by out-of-order frames or frame timestamps.
1149 TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) {
1150   const base::TimeDelta min_capture_period =
1151       base::TimeDelta::FromSeconds(1) / 30;
1152   const gfx::Rect damage_rect(0, 0, 1280, 720);
1153   const base::TimeDelta event_increment = min_capture_period * 2;
1154
1155   VideoCaptureOracle oracle(min_capture_period, true);
1156
1157   // Most basic scenario: Frames delivered one at a time, with no additional
1158   // captures in-between deliveries.
1159   base::TimeTicks t = InitialTestTimeTicks();
1160   int last_frame_number;
1161   base::TimeTicks ignored;
1162   for (int i = 0; i < 10; ++i) {
1163     t += event_increment;
1164     ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1165         VideoCaptureOracle::kCompositorUpdate,
1166         damage_rect, t));
1167     last_frame_number = oracle.RecordCapture();
1168     ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored));
1169   }
1170
1171   // Basic pipelined scenario: More than one frame in-flight at delivery points.
1172   for (int i = 0; i < 50; ++i) {
1173     const int num_in_flight = 1 + i % 3;
1174     for (int j = 0; j < num_in_flight; ++j) {
1175       t += event_increment;
1176       ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1177           VideoCaptureOracle::kCompositorUpdate,
1178           damage_rect, t));
1179       last_frame_number = oracle.RecordCapture();
1180     }
1181     for (int j = num_in_flight - 1; j >= 0; --j) {
1182       ASSERT_TRUE(oracle.CompleteCapture(last_frame_number - j, &ignored));
1183     }
1184   }
1185
1186   // Pipelined scenario with out-of-order delivery attempts rejected.
1187   for (int i = 0; i < 50; ++i) {
1188     const int num_in_flight = 1 + i % 3;
1189     for (int j = 0; j < num_in_flight; ++j) {
1190       t += event_increment;
1191       ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1192           VideoCaptureOracle::kCompositorUpdate,
1193           damage_rect, t));
1194       last_frame_number = oracle.RecordCapture();
1195     }
1196     ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored));
1197     for (int j = 1; j < num_in_flight; ++j) {
1198       ASSERT_FALSE(oracle.CompleteCapture(last_frame_number - j, &ignored));
1199     }
1200   }
1201 }
1202
1203 // Tests that VideoCaptureOracle transitions between using its two samplers in a
1204 // way that does not introduce severe jank, pauses, etc.
1205 TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) {
1206   const base::TimeDelta min_capture_period =
1207       base::TimeDelta::FromSeconds(1) / 30;
1208   const gfx::Rect animation_damage_rect(0, 0, 1280, 720);
1209   const base::TimeDelta event_increment = min_capture_period * 2;
1210
1211   VideoCaptureOracle oracle(min_capture_period, true);
1212
1213   // Run sequences of animation events and non-animation events through the
1214   // oracle.  As the oracle transitions between each sampler, make sure the
1215   // frame timestamps won't trip-up downstream consumers.
1216   base::TimeTicks t = InitialTestTimeTicks();
1217   base::TimeTicks last_frame_timestamp;
1218   for (int i = 0; i < 1000; ++i) {
1219     t += event_increment;
1220
1221     // For every 100 events, provide 50 that will cause the
1222     // AnimatedContentSampler to lock-in, followed by 50 that will cause it to
1223     // lock-out (i.e., the oracle will use the SmoothEventSampler instead).
1224     const bool provide_animated_content_event =
1225         (i % 100) >= 25 && (i % 100) < 75;
1226
1227     // Only the few events that trigger the lock-out transition should be
1228     // dropped, because the AnimatedContentSampler doesn't yet realize the
1229     // animation ended.  Otherwise, the oracle should always decide to sample
1230     // because one of its samplers says to.
1231     const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78;
1232     const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture(
1233         VideoCaptureOracle::kCompositorUpdate,
1234         provide_animated_content_event ? animation_damage_rect : gfx::Rect(),
1235         t);
1236     if (require_oracle_says_sample)
1237       ASSERT_TRUE(oracle_says_sample);
1238     if (!oracle_says_sample)
1239       continue;
1240
1241     const int frame_number = oracle.RecordCapture();
1242
1243     base::TimeTicks frame_timestamp;
1244     ASSERT_TRUE(oracle.CompleteCapture(frame_number, &frame_timestamp));
1245     ASSERT_FALSE(frame_timestamp.is_null());
1246     if (!last_frame_timestamp.is_null()) {
1247       const base::TimeDelta delta = frame_timestamp - last_frame_timestamp;
1248       EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds());
1249       // Right after the AnimatedContentSampler lock-out transition, there were
1250       // a few frames dropped, so allow a gap in the timestamps.  Otherwise, the
1251       // delta between frame timestamps should never be more than 2X the
1252       // |event_increment|.
1253       const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ?
1254           event_increment * 5 : event_increment * 2;
1255       EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds());
1256     }
1257     last_frame_timestamp = frame_timestamp;
1258   }
1259 }
1260
1261 }  // namespace content