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

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

Powered by Google App Engine
This is Rietveld 408576698