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

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

Issue 1021943002: Introduce cadence based VideoRendererAlgorithm. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comments. 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 "media/filters/video_renderer_algorithm.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 namespace media {
11
12 enum FrameSelector { NONE, CADENCE, COVERAGE, DRIFT };
13
14 VideoRendererAlgorithm::ReadyFrame::ReadyFrame(
15 const scoped_refptr<VideoFrame>& ready_frame)
16 : frame(ready_frame),
17 media_timestamp(ready_frame->timestamp()),
18 ideal_render_count(0),
19 render_count(0) {
20 }
21
22 VideoRendererAlgorithm::ReadyFrame::~ReadyFrame() {
23 }
24
25 bool VideoRendererAlgorithm::ReadyFrame::operator<(
26 const ReadyFrame& other) const {
27 return media_timestamp < other.media_timestamp;
28 }
29
30 VideoRendererAlgorithm::VideoRendererAlgorithm(
31 const TimeConverterCB& time_converter_cb)
32 : time_converter_cb_(time_converter_cb) {
33 Reset();
34 }
35
36 VideoRendererAlgorithm::~VideoRendererAlgorithm() {
37 }
38
39 scoped_refptr<VideoFrame> VideoRendererAlgorithm::Render(
40 base::TimeTicks deadline_min,
41 base::TimeTicks deadline_max,
42 int* frames_dropped) {
43 DCHECK(deadline_min < deadline_max);
44
45 if (frame_queue_.empty())
46 return nullptr;
47
48 // Once Render() is called |last_frame_index_| has meaning and should thus be
49 // preserved even if better frames come in before it due to out of order
50 // timestamps.
51 have_rendered_frames_ = true;
52
53 // Step 1: Update the current render interval for subroutines.
54 render_interval_ = deadline_max - deadline_min;
55
56 // Step 2: Figure out if any intervals have been skipped since the last call
57 // to Render(). If so, we assume the last frame provided was rendered during
58 // those intervals and adjust its render count appropriately.
59 AccountForMissedIntervals(deadline_min, deadline_max);
60
61 // Step 3: Update the wall clock timestamps and frame duration estimates for
62 // all frames currently in the |frame_queue_|.
63 if (!UpdateFrameStatistics()) {
64 DVLOG(2) << "Failed to update frame statistics.";
65 DCHECK(frame_queue_[last_frame_index_].frame);
66 return frame_queue_[last_frame_index_].frame;
67 }
68
69 FrameSelector frame_selector = NONE;
xhwang 2015/04/17 06:18:32 This is set but not used for anything?
DaleCurtis 2015/04/18 01:29:21 Was used for debug logs, removed.
70 base::TimeDelta selected_frame_drift;
71
72 // Step 4: Attempt to find the best frame by cadence.
73 int frame_to_render = -1;
74 if (ideal_cadence_) {
75 frame_selector = CADENCE;
76 frame_to_render = FindBestFrameByCadence();
77
78 if (frame_to_render >= 0) {
79 selected_frame_drift =
80 CalculateDriftForFrame(deadline_min, frame_to_render);
81 }
82 }
83
84 // Step 5: If no frame could be found by cadence or the selected frame exceeds
85 // acceptable drift, try to find the best frame by coverage of the deadline.
86 if (frame_to_render < 0 || selected_frame_drift > max_acceptable_drift_) {
87 frame_selector = COVERAGE;
88 int second_best_by_coverage = -1;
89 const int best_by_coverage = FindBestFrameByCoverage(
90 deadline_min, deadline_max, &second_best_by_coverage);
91
92 // If the frame was previously selected based on cadence, we're only here
93 // because the drift is too large, so even if the cadence frame has the best
94 // coverage, fallback to the second best by coverage if it has better drift.
95 if (frame_to_render == best_by_coverage && second_best_by_coverage >= 0 &&
96 CalculateDriftForFrame(deadline_min, second_best_by_coverage) <=
97 selected_frame_drift) {
98 frame_to_render = second_best_by_coverage;
99 } else {
100 frame_to_render = best_by_coverage;
101 }
102
103 if (frame_to_render >= 0) {
104 selected_frame_drift =
105 CalculateDriftForFrame(deadline_min, frame_to_render);
106 }
107 }
108
109 // Step 6: If _still_ no frame could be found by coverage, try to choose the
110 // least crappy option based on the drift from the deadline. If we're here the
111 // selection is going to be bad because it means no suitable frame has any
112 // coverage of the deadline interval.
113 if (frame_to_render < 0 || selected_frame_drift > max_acceptable_drift_) {
114 frame_selector = DRIFT;
115 frame_to_render = FindBestFrameByDrift(deadline_min);
116 selected_frame_drift =
117 CalculateDriftForFrame(deadline_min, frame_to_render);
118 }
119
120 last_render_had_glitch_ = selected_frame_drift > max_acceptable_drift_;
121 if (last_render_had_glitch_) {
122 DVLOG(2) << "Frame drift is too far: "
123 << selected_frame_drift.InMillisecondsF() << "ms";
124 }
125
126 DCHECK_GE(frame_to_render, 0);
127
128 // Drop some debugging information if a frame had poor cadence.
129 if (ideal_cadence_) {
130 const ReadyFrame& last_frame_info = frame_queue_[last_frame_index_];
131 if (frame_to_render != last_frame_index_ &&
132 last_frame_info.render_count < last_frame_info.ideal_render_count) {
133 last_render_had_glitch_ = true;
134 DVLOG(2) << "Underdisplayed frame " << last_frame_info.media_timestamp
135 << "; only " << last_frame_info.render_count
136 << " times instead of " << last_frame_info.ideal_render_count;
137 } else if (frame_to_render == last_frame_index_ &&
138 last_frame_info.render_count >=
139 last_frame_info.ideal_render_count) {
140 DVLOG(2) << "Overdisplayed frame " << last_frame_info.media_timestamp
141 << "; displayed " << last_frame_info.render_count + 1
142 << " times instead of " << last_frame_info.ideal_render_count;
143 last_render_had_glitch_ = true;
144 }
145 }
146
147 // Step 7: Drop frames which occur prior to the frame to be rendered. If any
148 // frame has a zero render count it should be reported as dropped.
149 if (frame_to_render > 0) {
150 if (frames_dropped) {
151 for (int i = 0; i < frame_to_render; ++i) {
152 if (!frame_queue_[i].render_count) {
153 DVLOG(2) << "Dropping undisplayed frame "
154 << frame_queue_[i].media_timestamp;
155 ++(*frames_dropped);
156 if (!fractional_cadence_)
157 last_render_had_glitch_ = true;
158 }
159 }
160 }
161
162 frame_queue_.erase(frame_queue_.begin(),
163 frame_queue_.begin() + frame_to_render);
164 }
165
166 // Step 8: Congratulations, the frame selection guantlet has been passed!
167 last_frame_index_ = 0;
168 ++frame_queue_.front().render_count;
169 DCHECK(frame_queue_.front().frame);
170 return frame_queue_.front().frame;
171 }
172
173 int VideoRendererAlgorithm::RemoveExpiredFrames(base::TimeTicks deadline_min) {
174 if (!UpdateFrameStatistics() || frame_duration_ == base::TimeDelta())
175 return 0;
176
177 DCHECK_GE(frame_queue_.size(), 2u);
178
179 // Finds and removes all frames which are too old to be used; I.e., the end of
180 // their display interval is further than |max_acceptable_drift_| from the
181 // given |deadline_min|.
182 int frames_to_expire = 0;
183 const base::TimeTicks mininum_frame_time =
184 deadline_min - max_acceptable_drift_ - frame_duration_;
185 for (; static_cast<size_t>(frames_to_expire) < frame_queue_.size() - 1;
186 ++frames_to_expire) {
187 if (frame_queue_[frames_to_expire].wall_clock_time >= mininum_frame_time)
188 break;
189 }
190
191 if (!frames_to_expire)
192 return 0;
193
194 frame_queue_.erase(frame_queue_.begin(),
195 frame_queue_.begin() + frames_to_expire);
196
197 last_frame_index_ = std::max(0, last_frame_index_ - frames_to_expire);
198 return frames_to_expire;
199 }
200
201 void VideoRendererAlgorithm::OnLastFrameDropped() {
202 DCHECK(!frame_queue_.empty());
203
204 // We only care if the frame was never rendered at all; otherwise we assume
205 // that the frame was redisplayed even if the renderer was told otherwise.
206 if (frame_queue_[last_frame_index_].render_count == 1)
207 frame_queue_[last_frame_index_].render_count = 0;
208 }
209
210 void VideoRendererAlgorithm::Reset() {
211 last_frame_index_ = ideal_cadence_ = fractional_cadence_ = 0;
212 last_detected_cadence_ = render_intervals_cadence_held_ = 0;
213 have_rendered_frames_ = last_render_had_glitch_ = false;
214 cadence_hysteresis_enabled_ = true;
215 last_deadline_max_ = base::TimeTicks();
216 frame_duration_ = render_interval_ = base::TimeDelta();
217 frame_queue_.clear();
218
219 // Default to ATSC IS/191 recommendations for maximum acceptable drift before
220 // we have enough frames to base the the maximum on frame duration.
221 max_acceptable_drift_ = base::TimeDelta::FromMilliseconds(15);
222 }
223
224 size_t VideoRendererAlgorithm::EffectiveFramesQueued() const {
225 if (!have_rendered_frames_ || !ideal_cadence_)
226 return frame_queue_.size();
227
228 size_t renderable_frame_count = 0;
229 for (size_t i = last_frame_index_; i < frame_queue_.size(); ++i) {
230 if (frame_queue_[i].ideal_render_count > 0)
231 ++renderable_frame_count;
232 }
233
234 return renderable_frame_count;
235 }
236
237 void VideoRendererAlgorithm::EnqueueFrame(
238 const scoped_refptr<VideoFrame>& frame) {
239 DCHECK(frame);
240 DCHECK(!frame->end_of_stream());
241
242 ReadyFrame ready_frame(frame);
243 auto it = frame_queue_.empty() ? frame_queue_.end()
244 : std::lower_bound(frame_queue_.begin(),
245 frame_queue_.end(), frame);
246
247 // If a frame was inserted before the first frame, update the index. On the
248 // next call to Render() it will be dropped.
249 if (it - frame_queue_.begin() <= last_frame_index_ && have_rendered_frames_)
250 ++last_frame_index_;
251
252 // The vast majority of cases should always append to the back, but in rare
253 // circumstance we get out of order timestamps, http://crbug.com/386551.
254 it = frame_queue_.insert(it, ready_frame);
255
256 // Project the current cadence calculations to include the new frame. These
257 // may not be accurate until the next Render() call. These updates are done
258 // to ensure EffectiveFramesQueued() returns a semi-reliable result.
259 if (ideal_cadence_ && !fractional_cadence_)
260 it->ideal_render_count = ideal_cadence_;
261 else if (fractional_cadence_)
262 UpdateFractionalCadenceForFrames(fractional_cadence_);
263
264 // Verify sorted order in debug mode.
265 for (size_t i = 0; i < frame_queue_.size() - 1; ++i) {
266 DCHECK(frame_queue_[i].frame->timestamp() <=
267 frame_queue_[i + 1].frame->timestamp());
268 }
269 }
270
271 void VideoRendererAlgorithm::AccountForMissedIntervals(
272 base::TimeTicks deadline_min,
273 base::TimeTicks deadline_max) {
274 const base::TimeTicks previous_deadline_max = last_deadline_max_;
275 last_deadline_max_ = deadline_max;
276
277 if (previous_deadline_max.is_null())
278 return;
279
280 const int64 render_cycle_count =
281 std::abs((deadline_min - previous_deadline_max) / render_interval_);
brianderson 2015/04/16 22:57:56 Is the abs here to round render_cycle_count down t
DaleCurtis 2015/04/17 02:38:12 Yes, fractional values should be zero. I see your
DaleCurtis 2015/04/18 01:29:21 Done.
282
283 // In the ideal case this value will be zero.
284 if (!render_cycle_count)
285 return;
286
287 DVLOG(2) << "Missed " << render_cycle_count << " Render() intervals.";
288
289 // Only update display count if the frame was displayed at all; it may not
290 // have been if OnFrameDropped() was called.
291 if (frame_queue_[last_frame_index_].render_count)
brianderson 2015/04/16 22:57:56 Does this need to be a loop for the case were rend
DaleCurtis 2015/04/17 02:38:12 No, the FindBestFrameByCadence() will handle overa
292 frame_queue_[last_frame_index_].render_count += render_cycle_count;
293 }
294
295 base::TimeDelta VideoRendererAlgorithm::CalculateTimeUntilGlitch(
296 double perfect_cadence,
297 double clamped_cadence,
298 bool fractional) {
299 if (clamped_cadence == 0.0)
300 return base::TimeDelta();
301
302 // Calculate the drift in microseconds for each frame we render at cadence
303 // instead of for its real duration.
304 const double rendered_frame_duration =
305 fractional ? render_interval_.InMicroseconds()
306 : clamped_cadence * render_interval_.InMicroseconds();
307
308 // When computing a fractional drift, we render the first of |clamped_cadence|
309 // frames and drop |clamped_cadence| - 1 frames. To make the calculations
310 // below work we need to project out the timestamp of the frame which would be
311 // displayed after accounting for those |clamped_cadence| frames.
312 const double actual_frame_duration =
313 fractional ? clamped_cadence * frame_duration_.InMicroseconds()
314 : frame_duration_.InMicroseconds();
315
316 const double rendered_vs_actual_duration_delta =
317 std::abs(rendered_frame_duration - actual_frame_duration);
318 if (rendered_vs_actual_duration_delta <
319 std::numeric_limits<double>::epsilon()) {
320 return kInfiniteDuration();
321 }
322
323 const double frames_rendered_before_drift_exhausted =
324 std::ceil(max_acceptable_drift_.InMicroseconds() /
325 rendered_vs_actual_duration_delta);
326
327 const base::TimeDelta time_until_glitch = base::TimeDelta::FromMicroseconds(
328 rendered_frame_duration * frames_rendered_before_drift_exhausted);
329
330 return time_until_glitch;
331 }
332
333 bool VideoRendererAlgorithm::UpdateFrameStatistics() {
334 // Figure out all current ready frame times at once so we minimize the drift
335 // relative to real time as the code below executes.
336 for (auto& frame_info : frame_queue_) {
337 frame_info.wall_clock_time =
338 time_converter_cb_.Run(frame_info.media_timestamp);
339
340 // If time stops or never started, exit immediately.
341 if (frame_info.wall_clock_time.is_null())
342 return false;
343 }
344
345 // Do we have enough frames to compute statistics?
346 const bool have_frame_duration = frame_duration_ != base::TimeDelta();
347 if (frame_queue_.size() < 2 && !have_frame_duration)
348 return true;
349
350 // Update |frame_duration_| estimate weighted towards the existing duration.
351 const base::TimeDelta wall_clock_delta = frame_queue_.back().wall_clock_time -
352 frame_queue_.front().wall_clock_time;
353 if (!have_frame_duration) {
354 frame_duration_ = wall_clock_delta / (frame_queue_.size() - 1);
355 } else {
356 frame_duration_ =
357 (frame_duration_ + wall_clock_delta) / frame_queue_.size();
358 }
359
360 // ITU-R BR.265 recommends a maximum acceptable drift of +/- half of the frame
361 // duration; there are other asymmetric, more lenient measures, that we're
362 // forgoing in favor of simplicity.
363 //
364 // We'll always allow at least 8.33ms of drift since literature suggests it's
365 // well below the floor of detection.
366 max_acceptable_drift_ =
367 std::max(frame_duration_ / 2, base::TimeDelta::FromSecondsD(1.0 / 120));
368
369 // The perfect cadence is the number of render intervals per frame, while the
370 // clamped cadence is the nearest matching integer cadence.
371 const double perfect_cadence =
372 frame_duration_.InSecondsF() / render_interval_.InSecondsF();
373 const int clamped_cadence = perfect_cadence + 0.5;
374
375 // Inverse cadence is checked to see if we have a fractional cadence which
376 // would look best if we consistently drop the same frames. A fractional
377 // cadence is something like 120fps content on a 60Hz display.
378 const double inverse_perfect_cadence = 1.0 / perfect_cadence;
379 const int clamped_inverse_cadence = inverse_perfect_cadence + 0.5;
380
381 const base::TimeDelta minimum_glitch_time =
382 base::TimeDelta::FromSeconds(kMinimumAcceptableTimeBetweenGlitchesSecs);
383
384 // See if the clamped cadence fits acceptable thresholds for exhausting drift.
brianderson 2015/04/16 22:57:56 Ok. I understand now how this block makes sure 29.
385 int new_cadence = 0, new_fractional_cadence = 0;
386 if (CalculateTimeUntilGlitch(perfect_cadence, clamped_cadence, false) >=
387 minimum_glitch_time) {
388 new_cadence = clamped_cadence;
389 } else if (CalculateTimeUntilGlitch(inverse_perfect_cadence,
390 clamped_inverse_cadence,
391 true) >= minimum_glitch_time) {
392 new_cadence = 1;
393 new_fractional_cadence = clamped_inverse_cadence;
394 }
395
396 // If hysteresis is enabled, require cadence to hold for some time before
397 // switching in or out of cadence based rendering mode.
398 if (cadence_hysteresis_enabled_) {
399 if (new_fractional_cadence) {
400 if (last_detected_cadence_ == new_fractional_cadence) {
401 ++render_intervals_cadence_held_;
402 } else {
403 last_detected_cadence_ = new_fractional_cadence;
404 render_intervals_cadence_held_ = 0;
405 }
406 } else {
407 if (last_detected_cadence_ == new_cadence) {
408 ++render_intervals_cadence_held_;
409 } else {
410 last_detected_cadence_ = new_cadence;
411 render_intervals_cadence_held_ = 0;
412 }
413 }
414
415 // To prevent oscillation of cadence selection, ensure cadence selections
416 // are held for some time before applying them. Value chosen arbitrarily.
417 const base::TimeDelta cadence_hysteresis =
418 base::TimeDelta::FromMilliseconds(100);
419 if (render_intervals_cadence_held_ * render_interval_ < cadence_hysteresis)
420 return true;
421 }
422
423 // No need to update cadence if there's been no change; cadence will be set
424 // as frames are added to the queue.
425 if (ideal_cadence_ == new_cadence &&
426 new_fractional_cadence == fractional_cadence_) {
427 return true;
428 }
429
430 if (new_fractional_cadence) {
431 UpdateFractionalCadenceForFrames(new_fractional_cadence);
432 } else {
433 // |new_cadence| may be zero at this point, which clears previous cadences.
434 for (auto& frame_info : frame_queue_)
435 frame_info.ideal_render_count = new_cadence;
436 }
437
438 // Thus far there appears to be no need for special 3:2 considerations, the
439 // smoothness scores seem to naturally fit that pattern based on maximizing
440 // frame coverage.
441
442 if (ideal_cadence_ != new_cadence) {
443 DVLOG(1) << "Cadence switch from " << ideal_cadence_ << " to "
444 << new_cadence << "; perfect_cadence: " << perfect_cadence;
445 }
446 if (fractional_cadence_ != new_fractional_cadence) {
447 DVLOG(1) << "Fractional cadence switch from " << fractional_cadence_
448 << " to " << new_fractional_cadence
449 << "; inverse_perfect_cadence: " << inverse_perfect_cadence;
450 }
451
452 ideal_cadence_ = new_cadence;
453 fractional_cadence_ = new_fractional_cadence;
454 return true;
455 }
456
457 void VideoRendererAlgorithm::UpdateFractionalCadenceForFrames(
458 int fractional_cadence) {
459 // Cadence is 1 for the first of every |fractional_cadence| frames and zero
460 // elsewhere.
461 for (size_t i = last_frame_index_; i < frame_queue_.size(); ++i) {
462 frame_queue_[i].ideal_render_count =
463 static_cast<int>((i - last_frame_index_) % fractional_cadence == 0);
464 }
465 }
466
467 int VideoRendererAlgorithm::FindBestFrameByCadence() {
468 DCHECK(!frame_queue_.empty());
469 if (!ideal_cadence_)
470 return -1;
471
472 const ReadyFrame& current_frame = frame_queue_[last_frame_index_];
473
474 // If the current frame is below cadence, we should prefer it.
475 if (current_frame.render_count < current_frame.ideal_render_count)
476 return last_frame_index_;
477
478 // If the current frame is on cadence, find the next displayable frame.
479 if (current_frame.render_count == current_frame.ideal_render_count) {
480 for (size_t i = last_frame_index_ + 1; i < frame_queue_.size(); ++i) {
481 if (frame_queue_[i].ideal_render_count > 0)
482 return i;
483 }
484 }
485
486 // The frame is overdisplayed or we don't have enough frames to find a better
487 // once by cadence, so return nothing.
488 return -1;
489 }
490
491 int VideoRendererAlgorithm::FindBestFrameByCoverage(
492 base::TimeTicks deadline_min,
493 base::TimeTicks deadline_max,
494 int* second_best) {
495 DCHECK(!frame_queue_.empty());
496
497 // Find the frame which covers the most of the interval [deadline_min,
498 // deadline_max]. Frames outside of the interval are considered to have 0%
499 // coverage, while those which completely overlap the interval have 100%.
500 double best_coverage = 0.0;
501 int best_frame_by_coverage = -1;
502 std::vector<double> coverage(frame_queue_.size(), 0.0);
503 for (size_t i = last_frame_index_; i < frame_queue_.size(); ++i) {
504 // Frames which start after the deadline interval have zero coverage.
505 if (frame_queue_[i].wall_clock_time > deadline_max)
506 continue;
507
508 // Clamp frame times to a maximum of |deadline_max|.
509 const base::TimeTicks next_frame_time = std::min(
510 deadline_max, i + 1 < frame_queue_.size()
511 ? frame_queue_[i + 1].wall_clock_time
512 : frame_queue_[i].wall_clock_time + frame_duration_);
513
514 // Frames entirely before the deadline interval have zero coverage.
515 if (next_frame_time < deadline_min)
516 continue;
517
518 // If we're here, the current frame overlaps the deadline in some way; so
519 // compute the duration of the interval which is covered.
520 const base::TimeDelta duration =
521 next_frame_time -
522 std::max(deadline_min, frame_queue_[i].wall_clock_time);
523
524 coverage[i] = duration.InSecondsF() / render_interval_.InSecondsF();
525 if (coverage[i] > best_coverage) {
526 best_frame_by_coverage = i;
527 best_coverage = coverage[i];
528 }
529 }
530
531 // Find the second best frame by coverage; done by zeroing the coverage for
532 // the previous best and recomputing the maximum.
533 *second_best = -1;
534 if (best_frame_by_coverage >= 0) {
535 coverage[best_frame_by_coverage] = 0.0;
536 auto it = std::max_element(coverage.begin(), coverage.end());
537 if (*it > 0)
538 *second_best = it - coverage.begin();
539 }
540
541 // If two frames have coverage within half a millisecond, prefer the earliest
542 // frame as having the best coverage. Value chosen via experimentation to
543 // ensure proper coverage calculation for 24fps in 60Hz where +/- 100us of
544 // jitter is present within the |render_interval_|. At 60Hz this works out to
545 // an allowed jitter of 3%.
546 const double kAllowableJitter = 500.0 / render_interval_.InMicroseconds();
547 if (*second_best >= 0 && best_frame_by_coverage > *second_best &&
548 std::abs(best_coverage - coverage[*second_best]) <= kAllowableJitter) {
549 std::swap(best_frame_by_coverage, *second_best);
550 }
551
552 // TODO(dalecurtis): We may want to make a better decision about what to do
brianderson 2015/04/16 22:57:56 Thanks for adding this TODO. Fixing this shouldn't
553 // when multiple frames have equivalent coverage over an interval. Jitter in
554 // the render interval may result in irregular frame selection which may be
555 // visible to a viewer.
556 //
557 // 23.974 fps and 24 fps in 60Hz are the most common susceptible rates, so
558 // extensive tests have been added to ensure these cases work properly.
559
560 return best_frame_by_coverage;
561 }
562
563 int VideoRendererAlgorithm::FindBestFrameByDrift(base::TimeTicks deadline_min) {
564 DCHECK(!frame_queue_.empty());
565
566 int best_frame_by_drift = -1;
567 base::TimeDelta min_drift = base::TimeDelta::Max();
568
569 for (size_t i = last_frame_index_; i < frame_queue_.size(); ++i) {
570 const base::TimeDelta drift = CalculateDriftForFrame(deadline_min, i);
571 if (drift < min_drift) {
572 min_drift = drift;
573 best_frame_by_drift = i;
574 }
575 }
576
577 return best_frame_by_drift;
578 }
579
580 base::TimeDelta VideoRendererAlgorithm::CalculateDriftForFrame(
581 base::TimeTicks deadline_min,
582 int frame_index) {
583 const ReadyFrame& frame = frame_queue_[frame_index];
584
585 // If the frame lies before the deadline, compute the delta against the end
586 // of the frame's correct display duration.
587 if (frame.wall_clock_time < deadline_min &&
588 frame.wall_clock_time + frame_duration_ < deadline_min) {
589 return deadline_min - (frame.wall_clock_time + frame_duration_);
590 }
591
592 // If the frame lies after the deadline, compute the delta against the start
593 // of the frame's correct display time.
594 if (frame.wall_clock_time > deadline_min)
595 return frame.wall_clock_time - deadline_min;
596
597 // Drift is zero for frames which overlap the deadline interval.
598 DCHECK_GE(deadline_min, frame.wall_clock_time);
599 DCHECK_GE(frame.wall_clock_time + frame_duration_, deadline_min);
600 return base::TimeDelta();
601 }
602
603 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698