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
17namespace content {
18namespace {
19
20bool AddEventAndConsiderSampling(SmoothEventSampler* sampler,
21                                 base::TimeTicks event_time) {
22  sampler->ConsiderPresentationEvent(event_time);
23  return sampler->ShouldSample();
24}
25
26void 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
38void 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
48base::TimeTicks InitialTestTimeTicks() {
49  return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
50}
51
52void 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.
86TEST(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.
125TEST(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.
170TEST(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.
219TEST(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.
254TEST(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
288TEST(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
312TEST(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
367namespace {
368
369struct DataPoint {
370  bool should_capture;
371  double increment_ms;
372};
373
374void 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
391TEST(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
419TEST(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
455TEST(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
497class 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
557TEST_F(AnimatedContentSamplerTest, ElectsNoneFromZeroDamageRects) {
558  EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
559}
560
561TEST_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
567TEST_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
575TEST_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
583TEST_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
591TEST_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
612TEST_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
635namespace {
636
637// A test scenario for AnimatedContentSamplerParameterizedTest.
638struct 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
658base::TimeDelta FpsAsPeriod(int frame_rate) {
659  return base::TimeDelta::FromSeconds(1) / frame_rate;
660}
661
662}  // namespace
663
664class 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.
807TEST_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.
870TEST_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.
942TEST_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.
1027TEST_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
1070INSTANTIATE_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.
1113TEST(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.
1149TEST(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.
1205TEST(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
1262