OLD | NEW |
| (Empty) |
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 "base/strings/stringprintf.h" | |
8 #include "testing/gtest/include/gtest/gtest.h" | |
9 | |
10 namespace content { | |
11 | |
12 namespace { | |
13 | |
14 base::TimeTicks InitialTestTimeTicks() { | |
15 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); | |
16 } | |
17 | |
18 } // namespace | |
19 | |
20 // Tests that VideoCaptureOracle filters out events whose timestamps are | |
21 // decreasing. | |
22 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) { | |
23 const base::TimeDelta min_capture_period = | |
24 base::TimeDelta::FromSeconds(1) / 30; | |
25 const gfx::Rect damage_rect(0, 0, 1280, 720); | |
26 const base::TimeDelta event_increment = min_capture_period * 2; | |
27 | |
28 VideoCaptureOracle oracle(min_capture_period); | |
29 | |
30 base::TimeTicks t = InitialTestTimeTicks(); | |
31 for (int i = 0; i < 10; ++i) { | |
32 t += event_increment; | |
33 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
34 VideoCaptureOracle::kCompositorUpdate, | |
35 damage_rect, t)); | |
36 } | |
37 | |
38 base::TimeTicks furthest_event_time = t; | |
39 for (int i = 0; i < 10; ++i) { | |
40 t -= event_increment; | |
41 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture( | |
42 VideoCaptureOracle::kCompositorUpdate, | |
43 damage_rect, t)); | |
44 } | |
45 | |
46 t = furthest_event_time; | |
47 for (int i = 0; i < 10; ++i) { | |
48 t += event_increment; | |
49 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
50 VideoCaptureOracle::kCompositorUpdate, | |
51 damage_rect, t)); | |
52 } | |
53 } | |
54 | |
55 // Tests that VideoCaptureOracle is enforcing the requirement that captured | |
56 // frames are delivered in order. Otherwise, downstream consumers could be | |
57 // tripped-up by out-of-order frames or frame timestamps. | |
58 TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) { | |
59 const base::TimeDelta min_capture_period = | |
60 base::TimeDelta::FromSeconds(1) / 30; | |
61 const gfx::Rect damage_rect(0, 0, 1280, 720); | |
62 const base::TimeDelta event_increment = min_capture_period * 2; | |
63 | |
64 VideoCaptureOracle oracle(min_capture_period); | |
65 | |
66 // Most basic scenario: Frames delivered one at a time, with no additional | |
67 // captures in-between deliveries. | |
68 base::TimeTicks t = InitialTestTimeTicks(); | |
69 int last_frame_number; | |
70 base::TimeTicks ignored; | |
71 for (int i = 0; i < 10; ++i) { | |
72 t += event_increment; | |
73 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
74 VideoCaptureOracle::kCompositorUpdate, | |
75 damage_rect, t)); | |
76 last_frame_number = oracle.RecordCapture(); | |
77 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored)); | |
78 } | |
79 | |
80 // Basic pipelined scenario: More than one frame in-flight at delivery points. | |
81 for (int i = 0; i < 50; ++i) { | |
82 const int num_in_flight = 1 + i % 3; | |
83 for (int j = 0; j < num_in_flight; ++j) { | |
84 t += event_increment; | |
85 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
86 VideoCaptureOracle::kCompositorUpdate, | |
87 damage_rect, t)); | |
88 last_frame_number = oracle.RecordCapture(); | |
89 } | |
90 for (int j = num_in_flight - 1; j >= 0; --j) { | |
91 ASSERT_TRUE( | |
92 oracle.CompleteCapture(last_frame_number - j, true, &ignored)); | |
93 } | |
94 } | |
95 | |
96 // Pipelined scenario with out-of-order delivery attempts rejected. | |
97 for (int i = 0; i < 50; ++i) { | |
98 const int num_in_flight = 1 + i % 3; | |
99 for (int j = 0; j < num_in_flight; ++j) { | |
100 t += event_increment; | |
101 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture( | |
102 VideoCaptureOracle::kCompositorUpdate, | |
103 damage_rect, t)); | |
104 last_frame_number = oracle.RecordCapture(); | |
105 } | |
106 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored)); | |
107 for (int j = 1; j < num_in_flight; ++j) { | |
108 ASSERT_FALSE( | |
109 oracle.CompleteCapture(last_frame_number - j, true, &ignored)); | |
110 } | |
111 } | |
112 } | |
113 | |
114 // Tests that VideoCaptureOracle transitions between using its two samplers in a | |
115 // way that does not introduce severe jank, pauses, etc. | |
116 TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) { | |
117 const base::TimeDelta min_capture_period = | |
118 base::TimeDelta::FromSeconds(1) / 30; | |
119 const gfx::Rect animation_damage_rect(0, 0, 1280, 720); | |
120 const base::TimeDelta event_increment = min_capture_period * 2; | |
121 | |
122 VideoCaptureOracle oracle(min_capture_period); | |
123 | |
124 // Run sequences of animation events and non-animation events through the | |
125 // oracle. As the oracle transitions between each sampler, make sure the | |
126 // frame timestamps won't trip-up downstream consumers. | |
127 base::TimeTicks t = InitialTestTimeTicks(); | |
128 base::TimeTicks last_frame_timestamp; | |
129 for (int i = 0; i < 1000; ++i) { | |
130 t += event_increment; | |
131 | |
132 // For every 100 events, provide 50 that will cause the | |
133 // AnimatedContentSampler to lock-in, followed by 50 that will cause it to | |
134 // lock-out (i.e., the oracle will use the SmoothEventSampler instead). | |
135 const bool provide_animated_content_event = | |
136 (i % 100) >= 25 && (i % 100) < 75; | |
137 | |
138 // Only the few events that trigger the lock-out transition should be | |
139 // dropped, because the AnimatedContentSampler doesn't yet realize the | |
140 // animation ended. Otherwise, the oracle should always decide to sample | |
141 // because one of its samplers says to. | |
142 const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78; | |
143 const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture( | |
144 VideoCaptureOracle::kCompositorUpdate, | |
145 provide_animated_content_event ? animation_damage_rect : gfx::Rect(), | |
146 t); | |
147 if (require_oracle_says_sample) | |
148 ASSERT_TRUE(oracle_says_sample); | |
149 if (!oracle_says_sample) { | |
150 ASSERT_EQ(base::TimeDelta(), oracle.estimated_frame_duration()); | |
151 continue; | |
152 } | |
153 ASSERT_LT(base::TimeDelta(), oracle.estimated_frame_duration()); | |
154 | |
155 const int frame_number = oracle.RecordCapture(); | |
156 | |
157 base::TimeTicks frame_timestamp; | |
158 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &frame_timestamp)); | |
159 ASSERT_FALSE(frame_timestamp.is_null()); | |
160 if (!last_frame_timestamp.is_null()) { | |
161 const base::TimeDelta delta = frame_timestamp - last_frame_timestamp; | |
162 EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds()); | |
163 // Right after the AnimatedContentSampler lock-out transition, there were | |
164 // a few frames dropped, so allow a gap in the timestamps. Otherwise, the | |
165 // delta between frame timestamps should never be more than 2X the | |
166 // |event_increment|. | |
167 const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ? | |
168 event_increment * 5 : event_increment * 2; | |
169 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds()); | |
170 } | |
171 last_frame_timestamp = frame_timestamp; | |
172 } | |
173 } | |
174 | |
175 // Tests that VideoCaptureOracle prevents timer polling from initiating | |
176 // simultaneous captures. | |
177 TEST(VideoCaptureOracleTest, SamplesOnlyOneOverdueFrameAtATime) { | |
178 const base::TimeDelta min_capture_period = | |
179 base::TimeDelta::FromSeconds(1) / 30; | |
180 const base::TimeDelta vsync_interval = | |
181 base::TimeDelta::FromSeconds(1) / 60; | |
182 const base::TimeDelta timer_interval = base::TimeDelta::FromMilliseconds( | |
183 VideoCaptureOracle::kMinTimerPollPeriodMillis); | |
184 | |
185 VideoCaptureOracle oracle(min_capture_period); | |
186 | |
187 // Have the oracle observe some compositor events. Simulate that each capture | |
188 // completes successfully. | |
189 base::TimeTicks t = InitialTestTimeTicks(); | |
190 base::TimeTicks ignored; | |
191 bool did_complete_a_capture = false; | |
192 for (int i = 0; i < 10; ++i) { | |
193 t += vsync_interval; | |
194 if (oracle.ObserveEventAndDecideCapture( | |
195 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) { | |
196 ASSERT_TRUE( | |
197 oracle.CompleteCapture(oracle.RecordCapture(), true, &ignored)); | |
198 did_complete_a_capture = true; | |
199 } | |
200 } | |
201 ASSERT_TRUE(did_complete_a_capture); | |
202 | |
203 // Start one more compositor-based capture, but do not notify of completion | |
204 // yet. | |
205 for (int i = 0; i <= 10; ++i) { | |
206 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!"; | |
207 t += vsync_interval; | |
208 if (oracle.ObserveEventAndDecideCapture( | |
209 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) { | |
210 break; | |
211 } | |
212 } | |
213 int frame_number = oracle.RecordCapture(); | |
214 | |
215 // Stop providing the compositor events and start providing timer polling | |
216 // events. No overdue samplings should be recommended because of the | |
217 // not-yet-complete compositor-based capture. | |
218 for (int i = 0; i < 10; ++i) { | |
219 t += timer_interval; | |
220 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture( | |
221 VideoCaptureOracle::kTimerPoll, gfx::Rect(), t)); | |
222 } | |
223 | |
224 // Now, complete the oustanding compositor-based capture and continue | |
225 // providing timer polling events. The oracle should start recommending | |
226 // sampling again. | |
227 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored)); | |
228 did_complete_a_capture = false; | |
229 for (int i = 0; i < 10; ++i) { | |
230 t += timer_interval; | |
231 if (oracle.ObserveEventAndDecideCapture( | |
232 VideoCaptureOracle::kTimerPoll, gfx::Rect(), t)) { | |
233 ASSERT_TRUE( | |
234 oracle.CompleteCapture(oracle.RecordCapture(), true, &ignored)); | |
235 did_complete_a_capture = true; | |
236 } | |
237 } | |
238 ASSERT_TRUE(did_complete_a_capture); | |
239 | |
240 // Start one more timer-based capture, but do not notify of completion yet. | |
241 for (int i = 0; i <= 10; ++i) { | |
242 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!"; | |
243 t += timer_interval; | |
244 if (oracle.ObserveEventAndDecideCapture( | |
245 VideoCaptureOracle::kTimerPoll, gfx::Rect(), t)) { | |
246 break; | |
247 } | |
248 } | |
249 frame_number = oracle.RecordCapture(); | |
250 | |
251 // Confirm that the oracle does not recommend sampling until the outstanding | |
252 // timer-based capture completes. | |
253 for (int i = 0; i < 10; ++i) { | |
254 t += timer_interval; | |
255 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture( | |
256 VideoCaptureOracle::kTimerPoll, gfx::Rect(), t)); | |
257 } | |
258 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored)); | |
259 for (int i = 0; i <= 10; ++i) { | |
260 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!"; | |
261 t += timer_interval; | |
262 if (oracle.ObserveEventAndDecideCapture( | |
263 VideoCaptureOracle::kTimerPoll, gfx::Rect(), t)) { | |
264 break; | |
265 } | |
266 } | |
267 } | |
268 | |
269 } // namespace content | |
OLD | NEW |