Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(380)

Side by Side Diff: content/browser/media/capture/video_capture_oracle_unittest.cc

Issue 1109603003: Clean-up: Break sampler classes into their own files. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/media/capture/video_capture_oracle.cc ('k') | content/content_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/media/capture/video_capture_oracle.h" 5 #include "content/browser/media/capture/video_capture_oracle.h"
6 6
7 #include <cstdlib>
8 #include <utility>
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h" 7 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h" 8 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h"
16 9
17 namespace content { 10 namespace content {
11
18 namespace { 12 namespace {
19 13
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() { 14 base::TimeTicks InitialTestTimeTicks() {
49 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); 15 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
50 } 16 }
51 17
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 18 } // namespace
83 19
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, 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, 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, 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, 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, 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, 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 namespace {
313
314 struct DataPoint {
315 bool should_capture;
316 double increment_ms;
317 };
318
319 void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
320 size_t num_data_points,
321 SmoothEventSampler* sampler) {
322 base::TimeTicks t = InitialTestTimeTicks();
323 for (size_t i = 0; i < num_data_points; ++i) {
324 t += base::TimeDelta::FromMicroseconds(
325 static_cast<int64>(data_points[i].increment_ms * 1000));
326 ASSERT_EQ(data_points[i].should_capture,
327 AddEventAndConsiderSampling(sampler, t))
328 << "at data_points[" << i << ']';
329 if (data_points[i].should_capture)
330 sampler->RecordSample();
331 }
332 }
333
334 } // namespace
335
336 TEST(SmoothEventSamplerTest, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz) {
337 // Actual capturing of timing data: Initial instability as a 24 FPS video was
338 // started from a still screen, then clearly followed by steady-state.
339 static const DataPoint data_points[] = {
340 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
341 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
342 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
343 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
344 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
345 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
346 { false, 0 }, { true, 50.161 }, { false, 0 }, { true, 33.44 },
347 { true, 16.72 }, { false, 16.721 }, { true, 66.881 }, { false, 0 },
348 { true, 33.441 }, { true, 16.72 }, { true, 50.16 }, { true, 16.72 },
349 { false, 16.721 }, { true, 50.161 }, { true, 50.16 }, { false, 0 },
350 { true, 33.441 }, { true, 50.337 }, { true, 50.183 }, { true, 16.722 },
351 { true, 50.161 }, { true, 33.441 }, { true, 50.16 }, { true, 33.441 },
352 { true, 50.16 }, { true, 33.441 }, { true, 50.16 }, { true, 33.44 },
353 { true, 50.161 }, { true, 50.16 }, { true, 33.44 }, { true, 33.441 },
354 { true, 50.16 }, { true, 50.161 }, { true, 33.44 }, { true, 33.441 },
355 { true, 50.16 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
356 { true, 50.161 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
357 { true, 83.601 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }
358 };
359
360 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
361 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
362 }
363
364 TEST(SmoothEventSamplerTest, DrawingAt30FpsWith60HzVsyncSampledAt30Hertz) {
365 // Actual capturing of timing data: Initial instability as a 30 FPS video was
366 // started from a still screen, then followed by steady-state. Drawing
367 // framerate from the video rendering was a bit volatile, but averaged 30 FPS.
368 static const DataPoint data_points[] = {
369 { true, 2407.69 }, { true, 16.733 }, { true, 217.362 }, { true, 33.441 },
370 { true, 33.44 }, { true, 33.44 }, { true, 33.441 }, { true, 33.44 },
371 { true, 33.44 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 },
372 { true, 16.721 }, { true, 33.44 }, { false, 0 }, { true, 50.161 },
373 { true, 50.16 }, { false, 0 }, { true, 50.161 }, { true, 33.44 },
374 { true, 16.72 }, { false, 0 }, { false, 16.72 }, { true, 66.881 },
375 { false, 0 }, { true, 33.44 }, { true, 16.72 }, { true, 50.161 },
376 { false, 0 }, { true, 33.538 }, { true, 33.526 }, { true, 33.447 },
377 { true, 33.445 }, { true, 33.441 }, { true, 16.721 }, { true, 33.44 },
378 { true, 33.44 }, { true, 50.161 }, { true, 16.72 }, { true, 33.44 },
379 { true, 33.441 }, { true, 33.44 }, { false, 0 }, { false, 16.72 },
380 { true, 66.881 }, { true, 16.72 }, { false, 16.72 }, { true, 50.16 },
381 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 33.44 },
382 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { false, 0 },
383 { true, 33.44 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
384 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 66.88 },
385 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
386 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
387 { true, 16.72 }, { true, 50.161 }, { false, 0 }, { true, 50.16 },
388 { false, 0.001 }, { true, 16.721 }, { true, 66.88 }, { true, 33.44 },
389 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
390 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 66.881 },
391 { true, 33.44 }, { true, 16.72 }, { true, 33.441 }, { false, 16.72 },
392 { true, 66.88 }, { true, 16.721 }, { true, 50.16 }, { true, 33.44 },
393 { true, 16.72 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 }
394 };
395
396 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
397 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
398 }
399
400 TEST(SmoothEventSamplerTest, DrawingAt60FpsWith60HzVsyncSampledAt30Hertz) {
401 // Actual capturing of timing data: WebGL Acquarium demo
402 // (http://webglsamples.googlecode.com/hg/aquarium/aquarium.html) which ran
403 // between 55-60 FPS in the steady-state.
404 static const DataPoint data_points[] = {
405 { true, 16.72 }, { true, 16.72 }, { true, 4163.29 }, { true, 50.193 },
406 { true, 117.041 }, { true, 50.161 }, { true, 50.16 }, { true, 33.441 },
407 { true, 50.16 }, { true, 33.44 }, { false, 0 }, { false, 0 },
408 { true, 50.161 }, { true, 83.601 }, { true, 50.16 }, { true, 16.72 },
409 { true, 33.441 }, { false, 16.72 }, { true, 50.16 }, { true, 16.72 },
410 { false, 0.001 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
411 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
412 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
413 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
414 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
415 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
416 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 33.44 },
417 { false, 0 }, { true, 16.721 }, { true, 50.161 }, { false, 0 },
418 { true, 33.44 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
419 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
420 { true, 50.16 }, { false, 0 }, { true, 16.721 }, { true, 33.44 },
421 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
422 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
423 { false, 0 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
424 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
425 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { true, 33.441 },
426 { false, 0 }, { true, 33.44 }, { true, 33.441 }, { false, 0 },
427 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
428 { true, 16.721 }, { true, 50.161 }, { false, 0 }, { true, 16.72 },
429 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 33.44 },
430 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
431 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
432 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
433 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
434 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
435 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
436 };
437
438 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
439 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
440 }
441
442 class AnimatedContentSamplerTest : public ::testing::Test {
443 public:
444 AnimatedContentSamplerTest() {}
445 ~AnimatedContentSamplerTest() override {}
446
447 void SetUp() override {
448 const base::TimeDelta since_epoch =
449 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
450 rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds()));
451 sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod()));
452 }
453
454 protected:
455 // Overridden by subclass for parameterized tests.
456 virtual base::TimeDelta GetMinCapturePeriod() const {
457 return base::TimeDelta::FromSeconds(1) / 30;
458 }
459
460 AnimatedContentSampler* sampler() const {
461 return sampler_.get();
462 }
463
464 int GetRandomInRange(int begin, int end) {
465 const int len = end - begin;
466 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
467 return begin + rand_offset;
468 }
469
470 gfx::Rect GetRandomDamageRect() {
471 return gfx::Rect(0, 0, GetRandomInRange(1, 100), GetRandomInRange(1, 100));
472 }
473
474 gfx::Rect GetContentDamageRect() {
475 // This must be distinct from anything GetRandomDamageRect() could return.
476 return gfx::Rect(0, 0, 1280, 720);
477 }
478
479 // Directly inject an observation. Only used to test
480 // ElectMajorityDamageRect().
481 void ObserveDamageRect(const gfx::Rect& damage_rect) {
482 sampler_->observations_.push_back(
483 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
484 }
485
486 gfx::Rect ElectMajorityDamageRect() const {
487 return sampler_->ElectMajorityDamageRect();
488 }
489
490 private:
491 // Note: Not using base::RandInt() because it is horribly slow on debug
492 // builds. The following is a very simple, deterministic LCG:
493 int NextRandomInt() {
494 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
495 return rand_seed_;
496 }
497
498 int rand_seed_;
499 scoped_ptr<AnimatedContentSampler> sampler_;
500 };
501
502 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromZeroDamageRects) {
503 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
504 }
505
506 TEST_F(AnimatedContentSamplerTest, ElectsMajorityFromOneDamageRect) {
507 const gfx::Rect the_one_rect(0, 0, 1, 1);
508 ObserveDamageRect(the_one_rect);
509 EXPECT_EQ(the_one_rect, ElectMajorityDamageRect());
510 }
511
512 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromTwoDamageRectsOfSameArea) {
513 const gfx::Rect one_rect(0, 0, 1, 1);
514 const gfx::Rect another_rect(1, 1, 1, 1);
515 ObserveDamageRect(one_rect);
516 ObserveDamageRect(another_rect);
517 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
518 }
519
520 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_1) {
521 const gfx::Rect one_rect(0, 0, 1, 1);
522 const gfx::Rect another_rect(0, 0, 2, 2);
523 ObserveDamageRect(one_rect);
524 ObserveDamageRect(another_rect);
525 EXPECT_EQ(another_rect, ElectMajorityDamageRect());
526 }
527
528 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_2) {
529 const gfx::Rect one_rect(0, 0, 2, 2);
530 const gfx::Rect another_rect(0, 0, 1, 1);
531 ObserveDamageRect(one_rect);
532 ObserveDamageRect(another_rect);
533 EXPECT_EQ(one_rect, ElectMajorityDamageRect());
534 }
535
536 TEST_F(AnimatedContentSamplerTest, ElectsSameAsMooreDemonstration) {
537 // A more complex sequence (from Moore's web site): Three different Rects with
538 // the same area, but occurring a different number of times. C should win the
539 // vote.
540 const gfx::Rect rect_a(0, 0, 1, 4);
541 const gfx::Rect rect_b(1, 1, 4, 1);
542 const gfx::Rect rect_c(2, 2, 2, 2);
543 for (int i = 0; i < 3; ++i)
544 ObserveDamageRect(rect_a);
545 for (int i = 0; i < 2; ++i)
546 ObserveDamageRect(rect_c);
547 for (int i = 0; i < 2; ++i)
548 ObserveDamageRect(rect_b);
549 for (int i = 0; i < 3; ++i)
550 ObserveDamageRect(rect_c);
551 ObserveDamageRect(rect_b);
552 for (int i = 0; i < 2; ++i)
553 ObserveDamageRect(rect_c);
554 EXPECT_EQ(rect_c, ElectMajorityDamageRect());
555 }
556
557 TEST_F(AnimatedContentSamplerTest, Elects24FpsVideoInsteadOf48FpsSpinner) {
558 // Scenario: 24 FPS 720x480 Video versus 48 FPS 96x96 "Busy Spinner"
559 const gfx::Rect video_rect(100, 100, 720, 480);
560 const gfx::Rect spinner_rect(360, 0, 96, 96);
561 for (int i = 0; i < 100; ++i) {
562 // |video_rect| occurs once for every two |spinner_rect|. Vary the order
563 // of events between the two:
564 ObserveDamageRect(video_rect);
565 ObserveDamageRect(spinner_rect);
566 ObserveDamageRect(spinner_rect);
567 ObserveDamageRect(video_rect);
568 ObserveDamageRect(spinner_rect);
569 ObserveDamageRect(spinner_rect);
570 ObserveDamageRect(spinner_rect);
571 ObserveDamageRect(video_rect);
572 ObserveDamageRect(spinner_rect);
573 ObserveDamageRect(spinner_rect);
574 ObserveDamageRect(video_rect);
575 ObserveDamageRect(spinner_rect);
576 }
577 EXPECT_EQ(video_rect, ElectMajorityDamageRect());
578 }
579
580 namespace {
581
582 // A test scenario for AnimatedContentSamplerParameterizedTest.
583 struct Scenario {
584 base::TimeDelta vsync_interval; // Reflects compositor's update rate.
585 base::TimeDelta min_capture_period; // Reflects maximum capture rate.
586 base::TimeDelta content_period; // Reflects content animation rate.
587
588 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c)
589 : vsync_interval(v), min_capture_period(m), content_period(c) {
590 CHECK(content_period >= vsync_interval)
591 << "Bad test params: Impossible to animate faster than the compositor.";
592 }
593 };
594
595 // Value printer for Scenario.
596 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
597 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
598 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
599 << ", content_period=" << s.content_period.InMicroseconds()
600 << " }";
601 }
602
603 base::TimeDelta FpsAsPeriod(int frame_rate) {
604 return base::TimeDelta::FromSeconds(1) / frame_rate;
605 }
606
607 } // namespace
608
609 class AnimatedContentSamplerParameterizedTest
610 : public AnimatedContentSamplerTest,
611 public ::testing::WithParamInterface<Scenario> {
612 public:
613 AnimatedContentSamplerParameterizedTest()
614 : count_dropped_frames_(0), count_sampled_frames_(0) {}
615 virtual ~AnimatedContentSamplerParameterizedTest() {}
616
617 protected:
618 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
619
620 base::TimeDelta GetMinCapturePeriod() const override {
621 return GetParam().min_capture_period;
622 }
623
624 // Generate a sequence of events from the compositor pipeline. The event
625 // times will all be at compositor vsync boundaries.
626 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
627 base::TimeTicks end,
628 bool include_content_frame_events,
629 bool include_random_events) {
630 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
631 base::TimeTicks next_content_time = begin - GetParam().content_period;
632 std::vector<Event> events;
633 for (base::TimeTicks compositor_time = begin; compositor_time < end;
634 compositor_time += GetParam().vsync_interval) {
635 if (include_content_frame_events && next_content_time < compositor_time) {
636 events.push_back(Event(GetContentDamageRect(), compositor_time));
637 next_content_time += GetParam().content_period;
638 } else if (include_random_events && GetRandomInRange(0, 1) == 0) {
639 events.push_back(Event(GetRandomDamageRect(), compositor_time));
640 }
641 }
642
643 DCHECK(!events.empty());
644 return events;
645 }
646
647 // Feed |events| through the sampler, and detect whether the expected
648 // lock-in/out transition occurs. Also, track and measure the frame drop
649 // ratio and check it against the expected drop rate.
650 void RunEventSequence(const std::vector<Event> events,
651 bool was_detecting_before,
652 bool is_detecting_after,
653 bool simulate_pipeline_back_pressure) {
654 gfx::Rect first_detected_region;
655
656 EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
657 bool has_detection_switched = false;
658 ResetFrameCounters();
659 for (std::vector<Event>::const_iterator i = events.begin();
660 i != events.end(); ++i) {
661 sampler()->ConsiderPresentationEvent(i->first, i->second);
662
663 // Detect when the sampler locks in/out, and that it stays that way for
664 // all further iterations of this loop.
665 if (!has_detection_switched &&
666 was_detecting_before != sampler()->HasProposal()) {
667 has_detection_switched = true;
668 }
669 ASSERT_EQ(
670 has_detection_switched ? is_detecting_after : was_detecting_before,
671 sampler()->HasProposal());
672
673 if (sampler()->HasProposal()) {
674 // Make sure the sampler doesn't flip-flop and keep proposing sampling
675 // based on locking into different regions.
676 if (first_detected_region.IsEmpty()) {
677 first_detected_region = sampler()->detected_region();
678 ASSERT_FALSE(first_detected_region.IsEmpty());
679 } else {
680 EXPECT_EQ(first_detected_region, sampler()->detected_region());
681 }
682
683 if (simulate_pipeline_back_pressure && GetRandomInRange(0, 2) == 0)
684 ClientCannotSampleFrame(*i);
685 else
686 ClientDoesWhatSamplerProposes(*i);
687 } else {
688 EXPECT_FALSE(sampler()->ShouldSample());
689 if (!simulate_pipeline_back_pressure || GetRandomInRange(0, 2) == 1)
690 sampler()->RecordSample(i->second);
691 }
692 }
693 EXPECT_EQ(is_detecting_after, sampler()->HasProposal());
694 ExpectFrameDropRatioIsCorrect();
695 }
696
697 void ResetFrameCounters() {
698 count_dropped_frames_ = 0;
699 count_sampled_frames_ = 0;
700 }
701
702 // Keep track what the sampler is proposing, and call RecordSample() if it
703 // proposes sampling |event|.
704 void ClientDoesWhatSamplerProposes(const Event& event) {
705 if (sampler()->ShouldSample()) {
706 EXPECT_EQ(GetContentDamageRect(), event.first);
707 sampler()->RecordSample(sampler()->frame_timestamp());
708 ++count_sampled_frames_;
709 } else if (event.first == GetContentDamageRect()) {
710 ++count_dropped_frames_;
711 }
712 }
713
714 // RecordSample() is not called, but for testing, keep track of what the
715 // sampler is proposing for |event|.
716 void ClientCannotSampleFrame(const Event& event) {
717 if (sampler()->ShouldSample()) {
718 EXPECT_EQ(GetContentDamageRect(), event.first);
719 ++count_sampled_frames_;
720 } else if (event.first == GetContentDamageRect()) {
721 ++count_dropped_frames_;
722 }
723 }
724
725 // Confirm the AnimatedContentSampler is not dropping more frames than
726 // expected, given current test parameters.
727 void ExpectFrameDropRatioIsCorrect() {
728 if (count_sampled_frames_ == 0) {
729 EXPECT_EQ(0, count_dropped_frames_);
730 return;
731 }
732 const double content_framerate =
733 1000000.0 / GetParam().content_period.InMicroseconds();
734 const double capture_framerate =
735 1000000.0 / GetParam().min_capture_period.InMicroseconds();
736 const double expected_drop_rate = std::max(
737 0.0, (content_framerate - capture_framerate) / capture_framerate);
738 const double actual_drop_rate =
739 static_cast<double>(count_dropped_frames_) / count_sampled_frames_;
740 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015);
741 }
742
743 private:
744 // These counters only include the frames with the desired content.
745 int count_dropped_frames_;
746 int count_sampled_frames_;
747 };
748
749 // Tests that the implementation locks in/out of frames containing stable
750 // animated content, whether or not random events are also simultaneously
751 // present.
752 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
753 // |begin| refers to the start of an event sequence in terms of the
754 // Compositor's clock.
755 base::TimeTicks begin = InitialTestTimeTicks();
756
757 // Provide random events and expect no lock-in.
758 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
759 RunEventSequence(GenerateEventSequence(begin, end, false, true),
760 false,
761 false,
762 false);
763 begin = end;
764
765 // Provide content frame events with some random events mixed-in, and expect
766 // the sampler to lock-in.
767 end = begin + base::TimeDelta::FromSeconds(5);
768 RunEventSequence(GenerateEventSequence(begin, end, true, true),
769 false,
770 true,
771 false);
772 begin = end;
773
774 // Continue providing content frame events without the random events mixed-in
775 // and expect the lock-in to hold.
776 end = begin + base::TimeDelta::FromSeconds(5);
777 RunEventSequence(GenerateEventSequence(begin, end, true, false),
778 true,
779 true,
780 false);
781 begin = end;
782
783 // Continue providing just content frame events and expect the lock-in to
784 // hold. Also simulate the capture pipeline experiencing back pressure.
785 end = begin + base::TimeDelta::FromSeconds(20);
786 RunEventSequence(GenerateEventSequence(begin, end, true, false),
787 true,
788 true,
789 true);
790 begin = end;
791
792 // Provide a half-second of random events only, and expect the lock-in to be
793 // broken.
794 end = begin + base::TimeDelta::FromMilliseconds(500);
795 RunEventSequence(GenerateEventSequence(begin, end, false, true),
796 true,
797 false,
798 false);
799 begin = end;
800
801 // Now, go back to providing content frame events, and expect the sampler to
802 // lock-in once again.
803 end = begin + base::TimeDelta::FromSeconds(5);
804 RunEventSequence(GenerateEventSequence(begin, end, true, false),
805 false,
806 true,
807 false);
808 begin = end;
809 }
810
811 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
812 // two animations of the same pixel change rate. VideoCaptureOracle should
813 // revert to using the SmoothEventSampler for these kinds of situations, as
814 // there is no "right answer" as to which animation to lock into.
815 TEST_P(AnimatedContentSamplerParameterizedTest,
816 DoesNotLockInToTwoCompetingAnimations) {
817 // Don't test when the event stream cannot indicate two separate content
818 // animations under the current test parameters.
819 if (GetParam().content_period < 2 * GetParam().vsync_interval)
820 return;
821
822 // Start the first animation and run for a bit, and expect the sampler to
823 // lock-in.
824 base::TimeTicks begin = InitialTestTimeTicks();
825 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
826 RunEventSequence(GenerateEventSequence(begin, end, true, false),
827 false,
828 true,
829 false);
830 begin = end;
831
832 // Now, keep the first animation and blend in an second animation of the same
833 // size and frame rate, but at a different position. This will should cause
834 // the sampler to enter an "undetected" state since it's unclear which
835 // animation should be locked into.
836 end = begin + base::TimeDelta::FromSeconds(20);
837 std::vector<Event> first_animation_events =
838 GenerateEventSequence(begin, end, true, false);
839 gfx::Rect second_animation_rect(
840 gfx::Point(0, GetContentDamageRect().height()),
841 GetContentDamageRect().size());
842 std::vector<Event> both_animations_events;
843 base::TimeDelta second_animation_offset = GetParam().vsync_interval;
844 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
845 i != first_animation_events.end(); ++i) {
846 both_animations_events.push_back(*i);
847 both_animations_events.push_back(
848 Event(second_animation_rect, i->second + second_animation_offset));
849 }
850 RunEventSequence(both_animations_events, true, false, false);
851 begin = end;
852
853 // Now, run just the first animation, and expect the sampler to lock-in once
854 // again.
855 end = begin + base::TimeDelta::FromSeconds(5);
856 RunEventSequence(GenerateEventSequence(begin, end, true, false),
857 false,
858 true,
859 false);
860 begin = end;
861
862 // Now, blend in the second animation again, but it has half the frame rate of
863 // the first animation and damage Rects with twice the area. This will should
864 // cause the sampler to enter an "undetected" state again. This tests that
865 // pixel-weighting is being accounted for in the sampler's logic.
866 end = begin + base::TimeDelta::FromSeconds(20);
867 first_animation_events = GenerateEventSequence(begin, end, true, false);
868 second_animation_rect.set_width(second_animation_rect.width() * 2);
869 both_animations_events.clear();
870 bool include_second_animation_frame = true;
871 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
872 i != first_animation_events.end(); ++i) {
873 both_animations_events.push_back(*i);
874 if (include_second_animation_frame) {
875 both_animations_events.push_back(
876 Event(second_animation_rect, i->second + second_animation_offset));
877 }
878 include_second_animation_frame = !include_second_animation_frame;
879 }
880 RunEventSequence(both_animations_events, true, false, false);
881 begin = end;
882 }
883
884 // Tests that the frame timestamps are smooth; meaning, that when run through a
885 // simulated compositor, each frame is held displayed for the right number of
886 // v-sync intervals.
887 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
888 // Generate 30 seconds of animated content events, run the events through
889 // AnimatedContentSampler, and record all frame timestamps being proposed
890 // once lock-in is continuous.
891 base::TimeTicks begin = InitialTestTimeTicks();
892 std::vector<Event> events = GenerateEventSequence(
893 begin,
894 begin + base::TimeDelta::FromSeconds(20),
895 true,
896 false);
897 typedef std::vector<base::TimeTicks> Timestamps;
898 Timestamps frame_timestamps;
899 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
900 ++i) {
901 sampler()->ConsiderPresentationEvent(i->first, i->second);
902 if (sampler()->HasProposal()) {
903 if (sampler()->ShouldSample()) {
904 frame_timestamps.push_back(sampler()->frame_timestamp());
905 sampler()->RecordSample(sampler()->frame_timestamp());
906 }
907 } else {
908 frame_timestamps.clear(); // Reset until continuous lock-in.
909 }
910 }
911 ASSERT_LE(2u, frame_timestamps.size());
912
913 // Iterate through the |frame_timestamps|, building a histogram counting the
914 // number of times each frame was displayed k times. For example, 10 frames
915 // of 30 Hz content on a 60 Hz v-sync interval should result in
916 // display_counts[2] == 10. Quit early if any one frame was obviously
917 // repeated too many times.
918 const int64 max_expected_repeats_per_frame = 1 +
919 std::max(GetParam().min_capture_period, GetParam().content_period) /
920 GetParam().vsync_interval;
921 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
922 base::TimeTicks last_present_time = frame_timestamps.front();
923 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
924 i != frame_timestamps.end(); ++i) {
925 const size_t num_vsync_intervals = static_cast<size_t>(
926 (*i - last_present_time) / GetParam().vsync_interval);
927 ASSERT_LT(0u, num_vsync_intervals);
928 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
929 ++display_counts[num_vsync_intervals];
930 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
931 }
932
933 // Analyze the histogram for an expected result pattern. If the frame
934 // timestamps are smooth, there should only be one or two buckets with
935 // non-zero counts and they should be next to each other. Because the clock
936 // precision for the event_times provided to the sampler is very granular
937 // (i.e., the vsync_interval), it's okay if other buckets have a tiny "stray"
938 // count in this test.
939 size_t highest_count = 0;
940 size_t second_highest_count = 0;
941 for (size_t repeats = 1; repeats < display_counts.size(); ++repeats) {
942 DVLOG(1) << "display_counts[" << repeats << "] is "
943 << display_counts[repeats];
944 if (display_counts[repeats] >= highest_count) {
945 second_highest_count = highest_count;
946 highest_count = display_counts[repeats];
947 } else if (display_counts[repeats] > second_highest_count) {
948 second_highest_count = display_counts[repeats];
949 }
950 }
951 size_t stray_count_remaining =
952 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
953 // Expect no more than 0.75% of frames fall outside the two main buckets.
954 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
955 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
956 if (display_counts[repeats] == highest_count) {
957 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
958 ++repeats;
959 } else if (display_counts[repeats] == second_highest_count) {
960 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
961 ++repeats;
962 } else {
963 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
964 stray_count_remaining -= display_counts[repeats];
965 }
966 }
967 }
968
969 // Tests that frame timestamps are "lightly pushed" back towards the original
970 // presentation event times, which tells us the AnimatedContentSampler can
971 // account for sources of timestamp drift and correct the drift.
972 TEST_P(AnimatedContentSamplerParameterizedTest,
973 FrameTimestampsConvergeTowardsEventTimes) {
974 const int max_drift_increment_millis = 3;
975
976 // Generate a full minute of events.
977 const base::TimeTicks begin = InitialTestTimeTicks();
978 const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1);
979 std::vector<Event> events = GenerateEventSequence(begin, end, true, false);
980
981 // Modify the event sequence so that 1-3 ms of additional drift is suddenly
982 // present every 100 events. This is meant to simulate that, external to
983 // AnimatedContentSampler, the video hardware vsync timebase is being
984 // refreshed and is showing severe drift from the system clock.
985 base::TimeDelta accumulated_drift;
986 for (size_t i = 1; i < events.size(); ++i) {
987 if (i % 100 == 0) {
988 accumulated_drift += base::TimeDelta::FromMilliseconds(
989 GetRandomInRange(1, max_drift_increment_millis + 1));
990 }
991 events[i].second += accumulated_drift;
992 }
993
994 // Run all the events through the sampler and track the last rewritten frame
995 // timestamp.
996 base::TimeTicks last_frame_timestamp;
997 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
998 ++i) {
999 sampler()->ConsiderPresentationEvent(i->first, i->second);
1000 if (sampler()->ShouldSample())
1001 last_frame_timestamp = sampler()->frame_timestamp();
1002 }
1003
1004 // If drift was accounted for, the |last_frame_timestamp| should be close to
1005 // the last event's timestamp.
1006 const base::TimeDelta total_error =
1007 events.back().second - last_frame_timestamp;
1008 const base::TimeDelta max_acceptable_error = GetParam().min_capture_period +
1009 base::TimeDelta::FromMilliseconds(max_drift_increment_millis);
1010 EXPECT_NEAR(0.0,
1011 total_error.InMicroseconds(),
1012 max_acceptable_error.InMicroseconds());
1013 }
1014
1015 INSTANTIATE_TEST_CASE_P(
1016 ,
1017 AnimatedContentSamplerParameterizedTest,
1018 ::testing::Values(
1019 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
1020 // Hz, and content video animates at 30, 25, or 24 Hz.
1021 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)),
1022 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)),
1023 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)),
1024
1025 // High frame rate content that leverages the Compositor's
1026 // capabilities, but capture is still at 30 Hz.
1027 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)),
1028 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)),
1029 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)),
1030
1031 // High frame rate content that leverages the Compositor's
1032 // capabilities, and capture is also a buttery 60 Hz.
1033 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)),
1034 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)),
1035 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)),
1036
1037 // On some platforms, the Compositor runs at 50 Hz.
1038 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)),
1039 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)),
1040 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)),
1041 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)),
1042 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)),
1043
1044 // Stable, but non-standard content frame rates.
1045 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)),
1046 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)),
1047 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)),
1048 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)),
1049 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)),
1050 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)),
1051 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)),
1052 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)),
1053 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)),
1054 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33))));
1055
1056 // Tests that VideoCaptureOracle filters out events whose timestamps are 20 // Tests that VideoCaptureOracle filters out events whose timestamps are
1057 // decreasing. 21 // decreasing.
1058 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) { 22 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) {
1059 const base::TimeDelta min_capture_period = 23 const base::TimeDelta min_capture_period =
1060 base::TimeDelta::FromSeconds(1) / 30; 24 base::TimeDelta::FromSeconds(1) / 30;
1061 const gfx::Rect damage_rect(0, 0, 1280, 720); 25 const gfx::Rect damage_rect(0, 0, 1280, 720);
1062 const base::TimeDelta event_increment = min_capture_period * 2; 26 const base::TimeDelta event_increment = min_capture_period * 2;
1063 27
1064 VideoCaptureOracle oracle(min_capture_period); 28 VideoCaptureOracle oracle(min_capture_period);
1065 29
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1197 // |event_increment|. 161 // |event_increment|.
1198 const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ? 162 const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ?
1199 event_increment * 5 : event_increment * 2; 163 event_increment * 5 : event_increment * 2;
1200 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds()); 164 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds());
1201 } 165 }
1202 last_frame_timestamp = frame_timestamp; 166 last_frame_timestamp = frame_timestamp;
1203 } 167 }
1204 } 168 }
1205 169
1206 } // namespace content 170 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/media/capture/video_capture_oracle.cc ('k') | content/content_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698