OLD | NEW |
---|---|
(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 | |
OLD | NEW |