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

Side by Side Diff: media/filters/video_renderer_algorithm_unittest.cc

Issue 1021943002: Introduce cadence based VideoRendererAlgorithm. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Switch to MovingAverage. 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
OLDNEW
(Empty)
1 // Copyright 2015 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 <cmath>
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/test/simple_test_tick_clock.h"
11 #include "media/base/video_frame_pool.h"
12 #include "media/base/wall_clock_time_source.h"
13 #include "media/filters/video_renderer_algorithm.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace media {
17
18 // Slows down the given |fps| according to NTSC field reduction standards; see
19 // http://en.wikipedia.org/wiki/Frame_rate#Digital_video_and_television
20 static double NTSC(double fps) {
21 return fps / 1.001;
22 }
23
24 // Helper class for generating TimeTicks in a sequence according to a frequency.
25 class TickGenerator {
26 public:
27 TickGenerator(base::TimeTicks base_timestamp, double hertz)
28 : tick_count_(0),
29 hertz_(hertz),
30 microseconds_per_tick_(base::Time::kMicrosecondsPerSecond / hertz),
31 base_time_(base_timestamp) {}
32
33 base::TimeDelta interval(int tick_count) const {
34 return base::TimeDelta::FromMicroseconds(tick_count *
35 microseconds_per_tick_);
36 }
37
38 base::TimeTicks current() const { return base_time_ + interval(tick_count_); }
39 base::TimeTicks step() { return step(1); }
40 base::TimeTicks step(int n) {
41 tick_count_ += n;
42 return current();
43 }
44
45 double hertz() const { return hertz_; }
46
47 void Reset(base::TimeTicks base_timestamp) {
48 base_time_ = base_timestamp;
49 tick_count_ = 0;
50 }
51
52 private:
53 // Track a tick count and seconds per tick value to ensure we don't drift too
54 // far due to accumulated errors during testing.
55 int64_t tick_count_;
56 const double hertz_;
57 const double microseconds_per_tick_;
58 base::TimeTicks base_time_;
59
60 DISALLOW_COPY_AND_ASSIGN(TickGenerator);
61 };
62
63 class VideoRendererAlgorithmTest : public testing::Test {
64 public:
65 VideoRendererAlgorithmTest()
66 : tick_clock_(new base::SimpleTestTickClock()),
67 algorithm_(base::Bind(&WallClockTimeSource::GetWallClockTime,
68 base::Unretained(&time_source_))) {
69 // Always start the TickClock at a non-zero value since null values have
70 // special connotations.
71 tick_clock_->Advance(base::TimeDelta::FromMicroseconds(10000));
72 time_source_.SetTickClockForTesting(
73 scoped_ptr<base::TickClock>(tick_clock_));
74 }
75 ~VideoRendererAlgorithmTest() override {}
76
77 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) {
78 const gfx::Size natural_size(8, 8);
79 return frame_pool_.CreateFrame(VideoFrame::YV12, natural_size,
80 gfx::Rect(natural_size), natural_size,
81 timestamp);
82 }
83
84 base::TimeDelta minimum_glitch_time() const {
85 return base::TimeDelta::FromSeconds(
86 VideoRendererAlgorithm::kMinimumAcceptableTimeBetweenGlitchesSecs);
87 }
88
89 base::TimeDelta max_acceptable_drift() const {
90 return algorithm_.max_acceptable_drift_;
91 }
92
93 void disable_cadence_hysteresis() {
94 algorithm_.cadence_estimator_.disable_cadence_hysteresis_for_testing();
95 }
96
97 bool last_render_had_glitch() const {
98 return algorithm_.last_render_had_glitch_;
99 }
100
101 bool is_using_cadence() const {
102 return algorithm_.cadence_estimator_.has_cadence();
103 }
104
105 bool IsUsingFractionalCadence() const {
106 return is_using_cadence() &&
107 !algorithm_.cadence_estimator_.GetCadenceForFrame(1);
108 }
109
110 size_t frames_queued() const { return algorithm_.frame_queue_.size(); }
111
112 int GetCadence(double frame_rate, double display_rate) {
113 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
114 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
115 time_source_.StartTicking();
116
117 // Enqueue enough frames for cadence detection.
118 size_t frames_dropped = 0;
119 disable_cadence_hysteresis();
120 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
121 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
122 EXPECT_TRUE(algorithm_.Render(display_tg.current(), display_tg.step(),
123 &frames_dropped));
124
125 // Store cadence before reseting the algorithm.
126 const int cadence = algorithm_.cadence_estimator_.get_cadence_for_testing();
127 time_source_.StopTicking();
128 algorithm_.Reset();
129 return cadence;
130 }
131
132 base::TimeDelta CalculateAbsoluteDriftForFrame(base::TimeTicks deadline_min,
133 int frame_index) {
134 return algorithm_.CalculateAbsoluteDriftForFrame(deadline_min, frame_index);
135 }
136
137 bool DriftOfLastRenderWasWithinTolerance(base::TimeTicks deadline_min) {
138 return CalculateAbsoluteDriftForFrame(deadline_min, 0) <=
139 algorithm_.max_acceptable_drift_;
140 }
141
142 // Allows tests to run a Render() loop with sufficient frames for the various
143 // rendering modes. Upon each Render() |render_test_func| will be called with
144 // the rendered frame and the number of frames dropped.
145 template <typename OnRenderCallback>
146 void RunFramePumpTest(bool reset,
147 TickGenerator* frame_tg,
148 TickGenerator* display_tg,
149 OnRenderCallback render_test_func) {
150 SCOPED_TRACE(base::StringPrintf("Rendering %.03f fps into %0.03f",
151 frame_tg->hertz(), display_tg->hertz()));
152 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
153 time_source_.StartTicking();
154
155 const bool fresh_algorithm = !algorithm_.have_rendered_frames_;
156
157 base::TimeDelta last_frame_timestamp = kNoTimestamp();
158 bool should_use_cadence = false;
159 int glitch_count = 0;
160 const base::TimeTicks start_time = tick_clock_->NowTicks();
161 while (tick_clock_->NowTicks() - start_time < minimum_glitch_time()) {
162 while (algorithm_.EffectiveFramesQueued() < 3 ||
163 frame_tg->current() - time_source_.CurrentMediaTime() <
164 base::TimeTicks()) {
165 algorithm_.EnqueueFrame(
166 CreateFrame(frame_tg->current() - base::TimeTicks()));
167 frame_tg->step();
168 }
169
170 size_t frames_dropped = 0;
171 const base::TimeTicks deadline_min = display_tg->current();
172 const base::TimeTicks deadline_max = display_tg->step();
173 scoped_refptr<VideoFrame> frame =
174 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
175
176 render_test_func(frame, frames_dropped);
177 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
178
179 if (HasFatalFailure())
180 return;
181
182 // Render() should always return a frame within drift tolerances.
183 ASSERT_TRUE(DriftOfLastRenderWasWithinTolerance(deadline_min));
184
185 // If we have a frame, the timestamps should always be monotonically
186 // increasing.
187 if (frame) {
188 if (last_frame_timestamp != kNoTimestamp())
189 ASSERT_LE(last_frame_timestamp, frame->timestamp());
190 else
191 last_frame_timestamp = frame->timestamp();
192 }
193
194 // Only verify certain properties for fresh instances.
195 if (fresh_algorithm) {
196 ASSERT_NEAR(frame_tg->interval(1).InMicroseconds(),
197 algorithm_.average_frame_duration().InMicroseconds(), 1);
198
199 if (is_using_cadence() && last_render_had_glitch())
200 ++glitch_count;
201
202 // Once cadence starts, it should never stop for the current set of
203 // tests.
204 if (is_using_cadence())
205 should_use_cadence = true;
206 ASSERT_EQ(is_using_cadence(), should_use_cadence);
207 }
208
209 // When there are no frames, we're not using cadence based selection, or
210 // a frame is under cadence the two queue size reports should be the same.
211 if (!is_using_cadence() || !frames_queued() ||
212 GetCurrentFrameDisplayCount() < GetCurrentFrameIdealDisplayCount()) {
213 ASSERT_EQ(frames_queued(), algorithm_.EffectiveFramesQueued());
214 } else if (is_using_cadence() && !IsUsingFractionalCadence()) {
215 // If there was no glitch in the last render, the two queue sizes should
216 // be off by exactly one frame; i.e., the current frame doesn't count.
217 if (!last_render_had_glitch())
218 ASSERT_EQ(frames_queued() - 1, algorithm_.EffectiveFramesQueued());
219 } else if (IsUsingFractionalCadence()) {
220 // The frame estimate should be off by at most one frame.
221 const size_t estimated_frames_queued =
222 frames_queued() /
223 algorithm_.cadence_estimator_.get_cadence_for_testing();
224 ASSERT_NEAR(algorithm_.EffectiveFramesQueued(), estimated_frames_queued,
225 1);
226 }
227 }
228
229 // When using cadence, the glitch count should be at most one for when
230 // rendering for the less than minimum_glitch_time().
231 if (fresh_algorithm && is_using_cadence())
232 ASSERT_LE(glitch_count, 1);
233
234 time_source_.StopTicking();
235 if (reset) {
236 algorithm_.Reset();
237 time_source_.SetMediaTime(base::TimeDelta());
238 }
239 }
240
241 int FindBestFrameByCoverage(base::TimeTicks deadline_min,
242 base::TimeTicks deadline_max,
243 int* second_best) {
244 return algorithm_.FindBestFrameByCoverage(deadline_min, deadline_max,
245 second_best);
246 }
247
248 int FindBestFrameByDrift(base::TimeTicks deadline_min,
249 base::TimeDelta* selected_frame_drift) {
250 return algorithm_.FindBestFrameByDrift(deadline_min, selected_frame_drift);
251 }
252
253 int GetCurrentFrameDropCount() const {
254 DCHECK_GT(frames_queued(), 0u);
255 return algorithm_.frame_queue_[algorithm_.last_frame_index_].drop_count;
256 }
257
258 int GetCurrentFrameDisplayCount() const {
259 DCHECK_GT(frames_queued(), 0u);
260 return algorithm_.frame_queue_[algorithm_.last_frame_index_].render_count;
261 }
262
263 int GetCurrentFrameIdealDisplayCount() const {
264 DCHECK_GT(frames_queued(), 0u);
265 return algorithm_.frame_queue_[algorithm_.last_frame_index_]
266 .ideal_render_count;
267 }
268
269 int AccountForMissedIntervals(base::TimeTicks deadline_min,
270 base::TimeTicks deadline_max) {
271 algorithm_.AccountForMissedIntervals(deadline_min, deadline_max);
272 return algorithm_.frames_queued() ? GetCurrentFrameDisplayCount() : -1;
273 }
274
275 protected:
276 VideoFramePool frame_pool_;
277 WallClockTimeSource time_source_;
278 base::SimpleTestTickClock* tick_clock_; // Owned by |time_source_|.
279 VideoRendererAlgorithm algorithm_;
280
281 private:
282 DISALLOW_COPY_AND_ASSIGN(VideoRendererAlgorithmTest);
283 };
284
285 TEST_F(VideoRendererAlgorithmTest, Empty) {
286 TickGenerator tg(tick_clock_->NowTicks(), 50);
287 size_t frames_dropped = 0;
288 EXPECT_EQ(0u, frames_queued());
289 EXPECT_FALSE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
290 EXPECT_EQ(0u, frames_dropped);
291 EXPECT_EQ(0u, frames_queued());
292 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
293 }
294
295 TEST_F(VideoRendererAlgorithmTest, Reset) {
296 TickGenerator tg(tick_clock_->NowTicks(), 50);
297 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
298 EXPECT_EQ(1u, frames_queued());
299 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
300 algorithm_.Reset();
301 EXPECT_EQ(0u, frames_queued());
302 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
303 }
304
305 TEST_F(VideoRendererAlgorithmTest, AccountForMissingIntervals) {
306 TickGenerator tg(tick_clock_->NowTicks(), 50);
307 time_source_.StartTicking();
308
309 // Disable hysteresis since AccountForMissingIntervals() only affects cadence
310 // based rendering.
311 disable_cadence_hysteresis();
312
313 // Simulate Render() called before any frames are present.
314 EXPECT_EQ(-1, AccountForMissedIntervals(tg.current(), tg.step()));
315
316 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
317 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
318 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
319 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
320
321 // Simulate Render() called before any frames have been rendered.
322 EXPECT_EQ(0, AccountForMissedIntervals(tg.current(), tg.step()));
323
324 // Render one frame (several are in the past and will be dropped).
325 base::TimeTicks deadline_min = tg.current();
326 base::TimeTicks deadline_max = tg.step();
327 size_t frames_dropped = 0;
328 scoped_refptr<VideoFrame> frame =
329 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
330 ASSERT_TRUE(frame);
331 EXPECT_EQ(tg.interval(2), frame->timestamp());
332 EXPECT_EQ(2u, frames_dropped);
333
334 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
335
336 // Now calling AccountForMissingIntervals with an interval which overlaps the
337 // previous should do nothing.
338 deadline_min += tg.interval(1) / 2;
339 deadline_max += tg.interval(1) / 2;
340 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
341
342 // Steping by 1.5 intervals, is not enough to increase the count.
343 deadline_min += tg.interval(1);
344 deadline_max += tg.interval(1);
345 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
346
347 // Calling it after a full skipped interval should increase the count by 1 for
348 // each skipped interval.
349 tg.step();
350 EXPECT_EQ(2, AccountForMissedIntervals(tg.current(), tg.step()));
351
352 // 4 because [tg.current(), tg.step()] now represents 2 additional intervals.
353 EXPECT_EQ(4, AccountForMissedIntervals(tg.current(), tg.step()));
354
355 // Frame should be way over cadence and no good frames remain, so last frame
356 // should be returned.
357 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
358 ASSERT_TRUE(frame);
359 EXPECT_EQ(tg.interval(3), frame->timestamp());
360 EXPECT_EQ(0u, frames_dropped);
361 }
362
363 TEST_F(VideoRendererAlgorithmTest, OnLastFrameDropped) {
364 TickGenerator frame_tg(base::TimeTicks(), 25);
365 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
366 time_source_.StartTicking();
367
368 // Disable hysteresis since OnLastFrameDropped() only affects cadence based
369 // rendering.
370 disable_cadence_hysteresis();
371
372 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
373 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
374 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
375
376 // Render one frame (several are in the past and will be dropped).
377 size_t frames_dropped = 0;
378 scoped_refptr<VideoFrame> frame = algorithm_.Render(
379 display_tg.current(), display_tg.step(), &frames_dropped);
380 ASSERT_TRUE(frame);
381 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
382 EXPECT_EQ(0u, frames_dropped);
383
384 // The frame should have its display count decremented once it's reported as
385 // dropped.
386 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
387 ASSERT_EQ(0, GetCurrentFrameDropCount());
388 algorithm_.OnLastFrameDropped();
389 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
390 ASSERT_EQ(1, GetCurrentFrameDropCount());
391
392 // Render the frame again and then force another drop.
393 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
394 &frames_dropped);
395 ASSERT_TRUE(frame);
396 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
397 EXPECT_EQ(0u, frames_dropped);
398
399 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
400 ASSERT_EQ(1, GetCurrentFrameDropCount());
401 algorithm_.OnLastFrameDropped();
402 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
403 ASSERT_EQ(2, GetCurrentFrameDropCount());
404
405 // The next Render() call should now count this frame as dropped.
406 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
407 &frames_dropped);
408 ASSERT_TRUE(frame);
409 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
410 EXPECT_EQ(1u, frames_dropped);
411 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
412 ASSERT_EQ(0, GetCurrentFrameDropCount());
413
414 // Rendering again should result in the same frame being displayed.
415 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
416 &frames_dropped);
417 ASSERT_TRUE(frame);
418 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
419 EXPECT_EQ(0u, frames_dropped);
420
421 // In this case, the drop count is less than the display count, so the frame
422 // should not be counted as dropped.
423 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
424 ASSERT_EQ(0, GetCurrentFrameDropCount());
425 algorithm_.OnLastFrameDropped();
426 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
427 ASSERT_EQ(1, GetCurrentFrameDropCount());
428
429 // The third frame should be rendered correctly now and the previous frame not
430 // counted as having been dropped.
431 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
432 &frames_dropped);
433 ASSERT_TRUE(frame);
434 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
435 EXPECT_EQ(0u, frames_dropped);
436 }
437
438 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueued) {
439 TickGenerator frame_tg(base::TimeTicks(), 50);
440 TickGenerator display_tg(tick_clock_->NowTicks(), 25);
441
442 // Disable hysteresis since EffectiveFramesQueued() is tested as part of the
443 // normal frame pump tests when cadence is not present.
444 disable_cadence_hysteresis();
445
446 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
447 time_source_.StartTicking();
448
449 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
450 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
451
452 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
453 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
454
455 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
456 EXPECT_EQ(3u, algorithm_.EffectiveFramesQueued());
457
458 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
459 EXPECT_EQ(4u, algorithm_.EffectiveFramesQueued());
460 EXPECT_EQ(4u, algorithm_.frames_queued());
461
462 // Render one frame which will detect cadence...
463 size_t frames_dropped = 0;
464 scoped_refptr<VideoFrame> frame = algorithm_.Render(
465 display_tg.current(), display_tg.step(), &frames_dropped);
466 ASSERT_TRUE(frame);
467 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
468 EXPECT_EQ(0u, frames_dropped);
469
470 // Fractional cadence should be detected and the count will decrease.
471 ASSERT_TRUE(is_using_cadence());
472 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
473 EXPECT_EQ(4u, algorithm_.frames_queued());
474
475 // Dropping the last rendered frame should do nothing, since the last frame
476 // is already excluded from the count if it has a display count of 1.
477 algorithm_.OnLastFrameDropped();
478 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
479 }
480
481 // The maximum acceptable drift should be updated once we have two frames.
482 TEST_F(VideoRendererAlgorithmTest, AcceptableDriftUpdated) {
483 TickGenerator tg(tick_clock_->NowTicks(), 50);
484
485 size_t frames_dropped = 0;
486 const base::TimeDelta original_drift = max_acceptable_drift();
487 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
488 EXPECT_EQ(1u, frames_queued());
489 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
490 EXPECT_EQ(original_drift, max_acceptable_drift());
491
492 // Time must be ticking to get wall clock times for frames.
493 time_source_.StartTicking();
494
495 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
496 EXPECT_EQ(2u, frames_queued());
497 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
498 EXPECT_NE(original_drift, max_acceptable_drift());
499 }
500
501 // Verifies behavior when time stops.
502 TEST_F(VideoRendererAlgorithmTest, TimeIsStopped) {
503 TickGenerator tg(tick_clock_->NowTicks(), 50);
504
505 // Prior to rendering the first frame, the algorithm should always return the
506 // first available frame.
507 size_t frames_dropped = 0;
508 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
509 EXPECT_EQ(1u, frames_queued());
510 scoped_refptr<VideoFrame> frame =
511 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
512 ASSERT_TRUE(frame);
513 EXPECT_EQ(tg.interval(0), frame->timestamp());
514 EXPECT_EQ(0u, frames_dropped);
515 EXPECT_EQ(1u, frames_queued());
516 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
517
518 // The same timestamp should be returned after time starts.
519 tick_clock_->Advance(tg.interval(1));
520 time_source_.StartTicking();
521 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
522 ASSERT_TRUE(frame);
523 EXPECT_EQ(tg.interval(0), frame->timestamp());
524 EXPECT_EQ(0u, frames_dropped);
525 EXPECT_EQ(1u, frames_queued());
526 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
527
528 // Ensure the next suitable frame is vended as time advances.
529 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
530 EXPECT_EQ(2u, frames_queued());
531 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
532 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
533 ASSERT_TRUE(frame);
534 EXPECT_EQ(tg.interval(1), frame->timestamp());
535 EXPECT_EQ(0u, frames_dropped);
536 EXPECT_EQ(1u, frames_queued());
537 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
538
539 // Once time stops ticking, any further frames shouldn't be returned, even if
540 // the interval requested more closely matches.
541 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
542 time_source_.StopTicking();
543 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
544 ASSERT_TRUE(frame);
545 EXPECT_EQ(tg.interval(1), frame->timestamp());
546 EXPECT_EQ(0u, frames_dropped);
547 EXPECT_EQ(2u, frames_queued());
548 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
549 }
550
551 // Verify frames inserted out of order end up in the right spot and are rendered
552 // according to the API contract.
553 TEST_F(VideoRendererAlgorithmTest, SortedFrameQueue) {
554 TickGenerator tg(tick_clock_->NowTicks(), 50);
555
556 // Ensure frames handed in out of order before time starts ticking are sorted
557 // and returned in the correct order upon Render().
558 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
559 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
560 EXPECT_EQ(2u, frames_queued());
561 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
562
563 time_source_.StartTicking();
564
565 // The first call should return the earliest frame appended.
566 size_t frames_dropped = 0;
567 scoped_refptr<VideoFrame> frame =
568 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
569 EXPECT_EQ(0u, frames_dropped);
570 EXPECT_EQ(tg.interval(2), frame->timestamp());
571 EXPECT_EQ(2u, frames_queued());
572 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
573
574 // Since a frame has already been rendered, queuing this frame and calling
575 // Render() should result in it being dropped; even though it's a better
576 // candidate for the desired interval.
577 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
578 EXPECT_EQ(3u, frames_queued());
579 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
580 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
581 EXPECT_EQ(1u, frames_dropped);
582 EXPECT_EQ(tg.interval(2), frame->timestamp());
583 EXPECT_EQ(2u, frames_queued());
584 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
585 }
586
587 // Run through integer cadence selection for 1, 2, 3, and 4.
588 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadence) {
589 const double kTestRates[][2] = {{60, 60}, {30, 60}, {25, 75}, {25, 100}};
590
591 for (const auto& test_rate : kTestRates) {
592 disable_cadence_hysteresis();
593
594 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
595 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
596
597 int actual_frame_pattern = 0;
598 const int desired_frame_pattern = test_rate[1] / test_rate[0];
599 scoped_refptr<VideoFrame> current_frame;
600 RunFramePumpTest(
601 true, &frame_tg, &display_tg,
602 [&current_frame, &actual_frame_pattern, desired_frame_pattern, this](
603 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
604 ASSERT_TRUE(frame);
605 ASSERT_EQ(0u, frames_dropped);
606
607 // Each frame should display for exactly it's desired cadence pattern.
608 if (!current_frame || current_frame == frame) {
609 actual_frame_pattern++;
610 } else {
611 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
612 actual_frame_pattern = 1;
613 }
614
615 current_frame = frame;
616 ASSERT_TRUE(is_using_cadence());
617 });
618
619 if (HasFatalFailure())
620 return;
621 }
622 }
623
624 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadenceOverdisplayed) {
625 TickGenerator frame_tg(base::TimeTicks(), 25);
626 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
627 time_source_.StartTicking();
628 disable_cadence_hysteresis();
629
630 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
631 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
632
633 // Render frames until we've exhausted available frames and the last frame is
634 // forced to be overdisplayed.
635 for (int i = 0; i < 5; ++i) {
636 size_t frames_dropped = 0;
637 scoped_refptr<VideoFrame> frame = algorithm_.Render(
638 display_tg.current(), display_tg.step(), &frames_dropped);
639 ASSERT_TRUE(frame);
640 EXPECT_EQ(frame_tg.interval(i < 4 ? i / 2 : 1), frame->timestamp());
641 EXPECT_EQ(0u, frames_dropped);
642 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
643 }
644
645 // Verify last frame is above cadence (2 in this case)
646 ASSERT_EQ(GetCurrentFrameIdealDisplayCount() + 1,
647 GetCurrentFrameDisplayCount());
648 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
649 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
650
651 // The next frame should only be displayed once, since the previous one was
652 // overdisplayed by one frame.
653 size_t frames_dropped = 0;
654 scoped_refptr<VideoFrame> frame = algorithm_.Render(
655 display_tg.current(), display_tg.step(), &frames_dropped);
656 ASSERT_TRUE(frame);
657 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
658 EXPECT_EQ(0u, frames_dropped);
659 ASSERT_EQ(1, GetCurrentFrameIdealDisplayCount());
660
661 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
662 &frames_dropped);
663 ASSERT_TRUE(frame);
664 EXPECT_EQ(frame_tg.interval(3), frame->timestamp());
665 EXPECT_EQ(0u, frames_dropped);
666 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
667 }
668
669 TEST_F(VideoRendererAlgorithmTest, BestFrameByCoverage) {
670 TickGenerator tg(tick_clock_->NowTicks(), 50);
671 time_source_.StartTicking();
672
673 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
674 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
675 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
676
677 base::TimeTicks deadline_min = tg.current();
678 base::TimeTicks deadline_max = deadline_min + tg.interval(1);
679
680 size_t frames_dropped = 0;
681 scoped_refptr<VideoFrame> frame =
682 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
683 ASSERT_TRUE(frame);
684 EXPECT_EQ(tg.interval(0), frame->timestamp());
685 EXPECT_EQ(0u, frames_dropped);
686
687 int second_best = 0;
688
689 // Coverage should be 1 for if the frame overlaps the interval entirely, no
690 // second best should be found.
691 EXPECT_EQ(0,
692 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
693 EXPECT_EQ(-1, second_best);
694
695 // 49/51 coverage for frame 0 and frame 1 should be within tolerance such that
696 // the earlier frame should still be chosen.
697 deadline_min = tg.current() + tg.interval(1) / 2 +
698 base::TimeDelta::FromMicroseconds(250);
699 deadline_max = deadline_min + tg.interval(1);
700 EXPECT_EQ(0,
701 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
702 EXPECT_EQ(1, second_best);
703
704 // 48/52 coverage should result in the second frame being chosen.
705 deadline_min = tg.current() + tg.interval(1) / 2 +
706 base::TimeDelta::FromMicroseconds(500);
707 deadline_max = deadline_min + tg.interval(1);
708 EXPECT_EQ(1,
709 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
710 EXPECT_EQ(0, second_best);
711
712 // Overlapping three frames should choose the one with the most coverage and
713 // the second best should be the earliest frame.
714 deadline_min = tg.current() + tg.interval(1) / 2;
715 deadline_max = deadline_min + tg.interval(2);
716 EXPECT_EQ(1,
717 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
718 EXPECT_EQ(0, second_best);
719
720 // Requesting coverage outside of all known frames should return -1 for both
721 // best indices.
722 deadline_min = tg.current() + tg.interval(frames_queued());
723 deadline_max = deadline_min + tg.interval(1);
724 EXPECT_EQ(-1,
725 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
726 EXPECT_EQ(-1, second_best);
727 }
728
729 TEST_F(VideoRendererAlgorithmTest, BestFrameByDriftAndDriftCalculations) {
730 TickGenerator tg(tick_clock_->NowTicks(), 50);
731 time_source_.StartTicking();
732
733 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
734 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
735
736 size_t frames_dropped = 0;
737 scoped_refptr<VideoFrame> frame = algorithm_.Render(
738 tg.current(), tg.current() + tg.interval(1), &frames_dropped);
739 ASSERT_TRUE(frame);
740 EXPECT_EQ(tg.interval(0), frame->timestamp());
741 EXPECT_EQ(0u, frames_dropped);
742
743 base::TimeDelta zero_drift, half_drift = tg.interval(1) / 2;
744 base::TimeDelta detected_drift;
745
746 // Frame_0 overlaps the deadline, Frame_1 is a full interval away.
747 base::TimeTicks deadline = tg.current();
748 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
749 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 1));
750 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
751 EXPECT_EQ(zero_drift, detected_drift);
752
753 // Frame_0 overlaps the deadline, Frame_1 is a half interval away.
754 deadline += half_drift;
755 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
756 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
757 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
758 EXPECT_EQ(zero_drift, detected_drift);
759
760 // Both frames overlap the deadline.
761 deadline += half_drift;
762 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
763 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
764 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
765 EXPECT_EQ(zero_drift, detected_drift);
766
767 // Frame_0 is half an interval away, Frame_1 overlaps the deadline.
768 deadline += half_drift;
769 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
770 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
771 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
772 EXPECT_EQ(zero_drift, detected_drift);
773
774 // Frame_0 is a full interval away, Frame_1 overlaps the deadline.
775 deadline += half_drift;
776 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 0));
777 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
778 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
779 EXPECT_EQ(zero_drift, detected_drift);
780
781 // Both frames are entirely before the deadline.
782 deadline += half_drift;
783 EXPECT_EQ(tg.interval(1) + half_drift,
784 CalculateAbsoluteDriftForFrame(deadline, 0));
785 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
786 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
787 EXPECT_EQ(half_drift, detected_drift);
788 }
789
790 // Run through fractional cadence selection for 1/2, 1/3, and 1/4.
791 TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) {
792 const double kTestRates[][2] = {{120, 60}, {72, 24}, {100, 25}};
793
794 for (const auto& test_rate : kTestRates) {
795 disable_cadence_hysteresis();
796
797 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
798 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
799
800 const size_t desired_drop_pattern = test_rate[0] / test_rate[1] - 1;
801 scoped_refptr<VideoFrame> current_frame;
802 RunFramePumpTest(
803 true, &frame_tg, &display_tg,
804 [&current_frame, desired_drop_pattern, this](
805 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
806 ASSERT_TRUE(frame);
807
808 // The first frame should have zero dropped frames, but each Render()
809 // call after should drop the same number of frames based on the
810 // fractional cadence.
811 if (!current_frame)
812 ASSERT_EQ(0u, frames_dropped);
813 else
814 ASSERT_EQ(desired_drop_pattern, frames_dropped);
815
816 ASSERT_NE(current_frame, frame);
817 ASSERT_TRUE(is_using_cadence());
818 current_frame = frame;
819 });
820
821 if (HasFatalFailure())
822 return;
823 }
824 }
825
826 // Verify a 3:2 frame pattern for 23.974fps in 60Hz; doubles as a test for best
827 // frame by coverage.
828 TEST_F(VideoRendererAlgorithmTest, FilmCadence) {
829 const double kTestRates[] = {NTSC(24), 24};
830
831 for (double frame_rate : kTestRates) {
832 scoped_refptr<VideoFrame> current_frame;
833 int actual_frame_pattern = 0, desired_frame_pattern = 3;
834
835 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
836 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
837
838 RunFramePumpTest(
839 true, &frame_tg, &display_tg,
840 [&current_frame, &actual_frame_pattern, &desired_frame_pattern, this](
841 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
842 ASSERT_TRUE(frame);
843 ASSERT_EQ(0u, frames_dropped);
844
845 if (!current_frame || current_frame == frame) {
846 actual_frame_pattern++;
847 } else {
848 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
849 actual_frame_pattern = 1;
850 desired_frame_pattern = (desired_frame_pattern == 3 ? 2 : 3);
851 }
852
853 current_frame = frame;
854 ASSERT_FALSE(is_using_cadence());
855 });
856
857 if (HasFatalFailure())
858 return;
859 }
860 }
861
862 // Spot check common display and frame rate pairs for correctness.
863 TEST_F(VideoRendererAlgorithmTest, CadenceCalculations) {
864 ASSERT_FALSE(GetCadence(24, 60));
865 ASSERT_FALSE(GetCadence(NTSC(24), 60));
866 ASSERT_FALSE(GetCadence(25, 60));
867 ASSERT_EQ(2, GetCadence(NTSC(30), 60));
868 ASSERT_EQ(2, GetCadence(30, 60));
869 ASSERT_FALSE(GetCadence(50, 60));
870 ASSERT_EQ(1, GetCadence(NTSC(60), 60));
871 ASSERT_EQ(2, GetCadence(120, 60));
872
873 // 50Hz is common in the EU.
874 ASSERT_FALSE(GetCadence(NTSC(24), 50));
875 ASSERT_FALSE(GetCadence(24, 50));
876 ASSERT_EQ(2, GetCadence(NTSC(25), 50));
877 ASSERT_EQ(2, GetCadence(25, 50));
878 ASSERT_FALSE(GetCadence(NTSC(30), 50));
879 ASSERT_FALSE(GetCadence(30, 50));
880 ASSERT_FALSE(GetCadence(NTSC(60), 50));
881 ASSERT_FALSE(GetCadence(60, 50));
882
883 ASSERT_FALSE(GetCadence(25, NTSC(60)));
884 ASSERT_EQ(2, GetCadence(120, NTSC(60)));
885 ASSERT_EQ(60, GetCadence(1, NTSC(60)));
886 }
887
888 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFrames) {
889 TickGenerator tg(tick_clock_->NowTicks(), 50);
890
891 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
892 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
893 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
894
895 time_source_.StartTicking();
896
897 size_t frames_dropped = 0;
898 scoped_refptr<VideoFrame> frame =
899 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
900 ASSERT_TRUE(frame);
901 EXPECT_EQ(tg.interval(0), frame->timestamp());
902 EXPECT_EQ(0u, frames_dropped);
903
904 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
905 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
906 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
907 algorithm_.EnqueueFrame(CreateFrame(tg.interval(4)));
908 EXPECT_EQ(5u, algorithm_.EffectiveFramesQueued());
909
910 tg.step(2);
911 ASSERT_EQ(2u, algorithm_.RemoveExpiredFrames(tg.current()));
912 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
913 EXPECT_EQ(1u, frames_dropped);
914 EXPECT_EQ(2u, frames_queued());
915 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
916 ASSERT_TRUE(frame);
917 EXPECT_EQ(tg.interval(3), frame->timestamp());
918 }
919
920 TEST_F(VideoRendererAlgorithmTest, CadenceBasedTest) {
921 // Common display rates.
922 const double kDisplayRates[] = {
923 NTSC(24),
924 24,
925 NTSC(25),
926 25,
927 NTSC(30),
928 30,
929 48,
930 NTSC(50),
931 50,
932 NTSC(60),
933 60,
934 75,
935 120,
936 144,
937 };
938
939 // List of common frame rate values. Values pulled from local test media,
940 // videostack test matrix, and Wikipedia.
941 const double kTestRates[] = {
942 1, 10, 12.5, 15, NTSC(24), 24, NTSC(25), 25,
943 NTSC(30), 30, 30.12, 48, NTSC(50), 50, 58.74, NTSC(60),
944 60, 72, 90, 100, 120, 144, 240, 300,
945 };
946
947 for (double display_rate : kDisplayRates) {
948 for (double frame_rate : kTestRates) {
949 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
950 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
951 RunFramePumpTest(
952 true, &frame_tg, &display_tg,
953 [](const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {});
954 if (HasFatalFailure())
955 return;
956 }
957 }
958 }
959
960 // Rotate through various playback rates and ensure algorithm adapts correctly.
961 TEST_F(VideoRendererAlgorithmTest, VariableFrameRateCadence) {
962 TickGenerator frame_tg(base::TimeTicks(), NTSC(30));
963 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
964
965 const double kTestRates[] = {1.0, 2, 0.215, 0.5, 1.0};
966 const bool kTestRateHasCadence[arraysize(kTestRates)] = {
967 true, true, false, true, true};
968
969 for (size_t i = 0; i < arraysize(kTestRates); ++i) {
970 const double playback_rate = kTestRates[i];
971 SCOPED_TRACE(base::StringPrintf("Playback Rate: %.03f", playback_rate));
972 time_source_.SetPlaybackRate(playback_rate);
973 RunFramePumpTest(false, &frame_tg, &display_tg,
974 [this](const scoped_refptr<VideoFrame>& frame,
975 size_t frames_dropped) {});
976 if (HasFatalFailure())
977 return;
978
979 ASSERT_EQ(kTestRateHasCadence[i], is_using_cadence());
980 }
981
982 // TODO(dalecurtis): It seems there should be some more things we can test
983 // here...
984 }
985
986 // Ensures media which only expresses timestamps in milliseconds, gets the right
987 // cadence detection.
988 TEST_F(VideoRendererAlgorithmTest, UglyTimestampsHaveCadence) {
989 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
990 time_source_.StartTicking();
991
992 // 59.94fps, timestamp deltas from https://youtu.be/byoLvAo9qjs
993 const int kBadTimestampsMs[] = {
994 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17, 16,
995 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17,
996 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17};
997
998 // Run throught ~1.6 seconds worth of frames.
999 bool cadence_detected = false;
1000 base::TimeDelta timestamp;
1001 for (size_t i = 0; i < arraysize(kBadTimestampsMs) * 2; ++i) {
1002 while (algorithm_.EffectiveFramesQueued() < 3) {
1003 algorithm_.EnqueueFrame(CreateFrame(timestamp));
1004 timestamp += base::TimeDelta::FromMilliseconds(
1005 kBadTimestampsMs[i % arraysize(kBadTimestampsMs)]);
1006 }
1007
1008 size_t frames_dropped = 0;
1009 algorithm_.Render(display_tg.current(), display_tg.step(), &frames_dropped);
1010 ASSERT_EQ(0u, frames_dropped);
1011
1012 // Cadence won't be detected immediately on this clip, but it will after
1013 // enough frames are encountered; after which it should not drop out of
1014 // cadence.
1015 if (is_using_cadence())
1016 cadence_detected = true;
1017
1018 if (cadence_detected)
1019 ASSERT_TRUE(is_using_cadence());
1020 }
1021 }
1022
1023 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698