OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 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/animated_content_sampler.h" |
6 | 6 |
7 #include <cstdlib> | 7 #include <cstdlib> |
8 #include <utility> | 8 #include <utility> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/strings/stringprintf.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/time/time.h" | 13 #include "base/time/time.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
15 #include "ui/gfx/geometry/rect.h" | 15 #include "ui/gfx/geometry/rect.h" |
16 | 16 |
17 namespace content { | 17 namespace content { |
| 18 |
18 namespace { | 19 namespace { |
19 | 20 |
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() { | 21 base::TimeTicks InitialTestTimeTicks() { |
49 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); | 22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); |
50 } | 23 } |
51 | 24 |
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 | 25 } // namespace |
83 | 26 |
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 { | 27 class AnimatedContentSamplerTest : public ::testing::Test { |
443 public: | 28 public: |
444 AnimatedContentSamplerTest() {} | 29 AnimatedContentSamplerTest() {} |
445 ~AnimatedContentSamplerTest() override {} | 30 ~AnimatedContentSamplerTest() override {} |
446 | 31 |
447 void SetUp() override { | 32 void SetUp() override { |
448 const base::TimeDelta since_epoch = | 33 const base::TimeDelta since_epoch = |
449 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch(); | 34 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch(); |
450 rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds())); | 35 rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds())); |
451 sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod())); | 36 sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod())); |
(...skipping 594 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1046 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)), | 631 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)), |
1047 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)), | 632 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)), |
1048 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)), | 633 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)), |
1049 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)), | 634 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)), |
1050 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)), | 635 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)), |
1051 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)), | 636 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)), |
1052 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)), | 637 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)), |
1053 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)), | 638 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)), |
1054 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33)))); | 639 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33)))); |
1055 | 640 |
1056 // Tests that VideoCaptureOracle filters out events whose timestamps are | |
1057 // decreasing. | |
1058 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) { | |
1059 const base::TimeDelta min_capture_period = | |
1060 base::TimeDelta::FromSeconds(1) / 30; | |
1061 const gfx::Rect damage_rect(0, 0, 1280, 720); | |
1062 const base::TimeDelta event_increment = min_capture_period * 2; | |
1063 | |
1064 VideoCaptureOracle oracle(min_capture_period); | |
1065 | |
1066 base::TimeTicks t = InitialTestTimeTicks(); | |
1067 for (int i = 0; i < 10; ++i) { | |
1068 t += event_increment; | |
1069 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
1070 VideoCaptureOracle::kCompositorUpdate, | |
1071 damage_rect, t)); | |
1072 } | |
1073 | |
1074 base::TimeTicks furthest_event_time = t; | |
1075 for (int i = 0; i < 10; ++i) { | |
1076 t -= event_increment; | |
1077 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture( | |
1078 VideoCaptureOracle::kCompositorUpdate, | |
1079 damage_rect, t)); | |
1080 } | |
1081 | |
1082 t = furthest_event_time; | |
1083 for (int i = 0; i < 10; ++i) { | |
1084 t += event_increment; | |
1085 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
1086 VideoCaptureOracle::kCompositorUpdate, | |
1087 damage_rect, t)); | |
1088 } | |
1089 } | |
1090 | |
1091 // Tests that VideoCaptureOracle is enforcing the requirement that captured | |
1092 // frames are delivered in order. Otherwise, downstream consumers could be | |
1093 // tripped-up by out-of-order frames or frame timestamps. | |
1094 TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) { | |
1095 const base::TimeDelta min_capture_period = | |
1096 base::TimeDelta::FromSeconds(1) / 30; | |
1097 const gfx::Rect damage_rect(0, 0, 1280, 720); | |
1098 const base::TimeDelta event_increment = min_capture_period * 2; | |
1099 | |
1100 VideoCaptureOracle oracle(min_capture_period); | |
1101 | |
1102 // Most basic scenario: Frames delivered one at a time, with no additional | |
1103 // captures in-between deliveries. | |
1104 base::TimeTicks t = InitialTestTimeTicks(); | |
1105 int last_frame_number; | |
1106 base::TimeTicks ignored; | |
1107 for (int i = 0; i < 10; ++i) { | |
1108 t += event_increment; | |
1109 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
1110 VideoCaptureOracle::kCompositorUpdate, | |
1111 damage_rect, t)); | |
1112 last_frame_number = oracle.RecordCapture(); | |
1113 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored)); | |
1114 } | |
1115 | |
1116 // Basic pipelined scenario: More than one frame in-flight at delivery points. | |
1117 for (int i = 0; i < 50; ++i) { | |
1118 const int num_in_flight = 1 + i % 3; | |
1119 for (int j = 0; j < num_in_flight; ++j) { | |
1120 t += event_increment; | |
1121 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
1122 VideoCaptureOracle::kCompositorUpdate, | |
1123 damage_rect, t)); | |
1124 last_frame_number = oracle.RecordCapture(); | |
1125 } | |
1126 for (int j = num_in_flight - 1; j >= 0; --j) { | |
1127 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number - j, &ignored)); | |
1128 } | |
1129 } | |
1130 | |
1131 // Pipelined scenario with out-of-order delivery attempts rejected. | |
1132 for (int i = 0; i < 50; ++i) { | |
1133 const int num_in_flight = 1 + i % 3; | |
1134 for (int j = 0; j < num_in_flight; ++j) { | |
1135 t += event_increment; | |
1136 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
1137 VideoCaptureOracle::kCompositorUpdate, | |
1138 damage_rect, t)); | |
1139 last_frame_number = oracle.RecordCapture(); | |
1140 } | |
1141 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored)); | |
1142 for (int j = 1; j < num_in_flight; ++j) { | |
1143 ASSERT_FALSE(oracle.CompleteCapture(last_frame_number - j, &ignored)); | |
1144 } | |
1145 } | |
1146 } | |
1147 | |
1148 // Tests that VideoCaptureOracle transitions between using its two samplers in a | |
1149 // way that does not introduce severe jank, pauses, etc. | |
1150 TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) { | |
1151 const base::TimeDelta min_capture_period = | |
1152 base::TimeDelta::FromSeconds(1) / 30; | |
1153 const gfx::Rect animation_damage_rect(0, 0, 1280, 720); | |
1154 const base::TimeDelta event_increment = min_capture_period * 2; | |
1155 | |
1156 VideoCaptureOracle oracle(min_capture_period); | |
1157 | |
1158 // Run sequences of animation events and non-animation events through the | |
1159 // oracle. As the oracle transitions between each sampler, make sure the | |
1160 // frame timestamps won't trip-up downstream consumers. | |
1161 base::TimeTicks t = InitialTestTimeTicks(); | |
1162 base::TimeTicks last_frame_timestamp; | |
1163 for (int i = 0; i < 1000; ++i) { | |
1164 t += event_increment; | |
1165 | |
1166 // For every 100 events, provide 50 that will cause the | |
1167 // AnimatedContentSampler to lock-in, followed by 50 that will cause it to | |
1168 // lock-out (i.e., the oracle will use the SmoothEventSampler instead). | |
1169 const bool provide_animated_content_event = | |
1170 (i % 100) >= 25 && (i % 100) < 75; | |
1171 | |
1172 // Only the few events that trigger the lock-out transition should be | |
1173 // dropped, because the AnimatedContentSampler doesn't yet realize the | |
1174 // animation ended. Otherwise, the oracle should always decide to sample | |
1175 // because one of its samplers says to. | |
1176 const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78; | |
1177 const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture( | |
1178 VideoCaptureOracle::kCompositorUpdate, | |
1179 provide_animated_content_event ? animation_damage_rect : gfx::Rect(), | |
1180 t); | |
1181 if (require_oracle_says_sample) | |
1182 ASSERT_TRUE(oracle_says_sample); | |
1183 if (!oracle_says_sample) | |
1184 continue; | |
1185 | |
1186 const int frame_number = oracle.RecordCapture(); | |
1187 | |
1188 base::TimeTicks frame_timestamp; | |
1189 ASSERT_TRUE(oracle.CompleteCapture(frame_number, &frame_timestamp)); | |
1190 ASSERT_FALSE(frame_timestamp.is_null()); | |
1191 if (!last_frame_timestamp.is_null()) { | |
1192 const base::TimeDelta delta = frame_timestamp - last_frame_timestamp; | |
1193 EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds()); | |
1194 // Right after the AnimatedContentSampler lock-out transition, there were | |
1195 // a few frames dropped, so allow a gap in the timestamps. Otherwise, the | |
1196 // delta between frame timestamps should never be more than 2X the | |
1197 // |event_increment|. | |
1198 const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ? | |
1199 event_increment * 5 : event_increment * 2; | |
1200 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds()); | |
1201 } | |
1202 last_frame_timestamp = frame_timestamp; | |
1203 } | |
1204 } | |
1205 | |
1206 } // namespace content | 641 } // namespace content |
OLD | NEW |