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_cadence_estimator.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 | |
10 namespace media { | |
11 | |
12 // To prevent oscillation in and out of cadence or between cadence values, we | |
13 // require some time to elapse before a cadence switch is accepted. | |
14 const int kMinimumCadenceDurationMs = 100; | |
15 | |
16 VideoCadenceEstimator::VideoCadenceEstimator( | |
17 base::TimeDelta minimum_time_until_glitch) | |
18 : cadence_hysteresis_threshold_( | |
19 base::TimeDelta::FromMilliseconds(kMinimumCadenceDurationMs)), | |
20 minimum_time_until_glitch_(minimum_time_until_glitch) { | |
21 Reset(); | |
22 } | |
23 | |
24 VideoCadenceEstimator::~VideoCadenceEstimator() { | |
25 } | |
26 | |
27 void VideoCadenceEstimator::Reset() { | |
28 cadence_ = fractional_cadence_ = 0; | |
29 pending_cadence_ = pending_fractional_cadence_ = 0; | |
30 render_intervals_cadence_held_ = 0; | |
31 } | |
32 | |
33 bool VideoCadenceEstimator::UpdateCadenceEstimate( | |
34 base::TimeDelta render_interval, | |
35 base::TimeDelta frame_duration, | |
36 base::TimeDelta max_acceptable_drift) { | |
37 DCHECK_GT(render_interval, base::TimeDelta()); | |
38 DCHECK_GT(frame_duration, base::TimeDelta()); | |
39 | |
40 base::TimeDelta time_until_cadence_glitch; | |
41 base::TimeDelta time_until_fractional_cadence_glitch; | |
42 | |
43 // See if the clamped cadence fits acceptable thresholds for exhausting drift. | |
44 int new_cadence = 0, new_fractional_cadence = 0; | |
45 if (CalculateCadence(render_interval, frame_duration, max_acceptable_drift, | |
46 false, &new_cadence, &time_until_cadence_glitch)) { | |
47 DCHECK(new_cadence); | |
48 } else if (CalculateCadence(render_interval, frame_duration, | |
49 max_acceptable_drift, true, | |
50 &new_fractional_cadence, | |
51 &time_until_fractional_cadence_glitch)) { | |
52 new_cadence = 1; | |
53 DCHECK(new_fractional_cadence); | |
54 } | |
55 | |
56 // Nothing changed, so do nothing. | |
57 if (new_cadence == cadence_ && | |
58 new_fractional_cadence == fractional_cadence_) { | |
59 // Clear cadence hold to pending values from accumulating incorrectly. | |
60 render_intervals_cadence_held_ = 0; | |
61 return false; | |
62 } | |
63 | |
64 // Wait until enough render intervals have elapsed before accepting the | |
65 // cadence change. Prevents oscillation of the cadence selection. | |
66 bool update_pending_cadence = true; | |
67 if ((new_cadence == pending_cadence_ && | |
68 new_fractional_cadence == pending_fractional_cadence_) || | |
69 cadence_hysteresis_threshold_ <= render_interval) { | |
70 if (++render_intervals_cadence_held_ * render_interval >= | |
71 cadence_hysteresis_threshold_) { | |
72 DVLOG(1) << "Cadence switch: (" << cadence_ << ", " << fractional_cadence_ | |
73 << ") -> (" << new_cadence << ", " << new_fractional_cadence | |
74 << ") :: (" << time_until_cadence_glitch << ", " | |
75 << time_until_fractional_cadence_glitch << ")"; | |
76 | |
77 cadence_ = new_cadence; | |
78 fractional_cadence_ = new_fractional_cadence; | |
79 return true; | |
80 } | |
81 | |
82 update_pending_cadence = false; | |
83 } | |
84 | |
85 DVLOG(2) << "Hysteresis prevented cadence switch: (" << cadence_ << ", " | |
86 << fractional_cadence_ << ") -> (" << new_cadence << ", " | |
87 << new_fractional_cadence << ") :: (" << time_until_cadence_glitch | |
88 << ", " << time_until_fractional_cadence_glitch << ")"; | |
89 | |
90 if (update_pending_cadence) { | |
91 pending_cadence_ = new_cadence; | |
92 pending_fractional_cadence_ = new_fractional_cadence; | |
93 render_intervals_cadence_held_ = 1; | |
94 } | |
95 | |
96 return false; | |
97 } | |
98 | |
99 bool VideoCadenceEstimator::CalculateCadence( | |
100 base::TimeDelta render_interval, | |
101 base::TimeDelta frame_duration, | |
102 base::TimeDelta max_acceptable_drift, | |
103 bool fractional, | |
104 int* cadence, | |
105 base::TimeDelta* time_until_glitch) { | |
106 // The perfect cadence is the number of render intervals per frame, while the | |
107 // clamped cadence is the nearest matching integer cadence. | |
108 // | |
109 // Fractional cadence is checked to see if we have a cadence which would look | |
110 // best if we consistently drop the same frames. | |
111 // | |
112 // As mentioned in the introduction, |perfect_cadence| is the ratio of the | |
113 // frame duration to render interval length; while |clamped_cadence| is the | |
114 // nearest integer value to |perfect_cadence|. When computing a fractional | |
115 // cadence (1/|perfect_cadence|), |fractional| must be set to true to ensure | |
116 // the rendered and actual frame durations are computed correctly. | |
117 const double perfect_cadence = | |
118 fractional ? render_interval.InSecondsF() / frame_duration.InSecondsF() | |
119 : frame_duration.InSecondsF() / render_interval.InSecondsF(); | |
120 const int clamped_cadence = perfect_cadence + 0.5; | |
121 if (!clamped_cadence) | |
122 return false; | |
123 | |
124 // Calculate the drift in microseconds for each frame we render at cadence | |
125 // instead of for its real duration. | |
126 const base::TimeDelta rendered_frame_duration = | |
127 fractional ? render_interval : clamped_cadence * render_interval; | |
128 | |
129 // When computing a fractional drift, we render the first of |clamped_cadence| | |
130 // frames and drop |clamped_cadence| - 1 frames. To make the calculations | |
131 // below work we need to project out the timestamp of the frame which would be | |
132 // rendered after accounting for those |clamped_cadence| frames. | |
133 const base::TimeDelta actual_frame_duration = | |
134 fractional ? clamped_cadence * frame_duration : frame_duration; | |
135 if (rendered_frame_duration == actual_frame_duration) { | |
136 *cadence = clamped_cadence; | |
137 return true; | |
138 } | |
139 | |
140 // Compute how long it'll take to exhaust the drift using |clamped_cadence|. | |
141 const double duration_delta = std::abs( | |
miu
2015/04/30 21:57:07
nit: Instead of std::abs((expr).InMicroseconds()),
DaleCurtis
2015/04/30 22:03:52
Done.
| |
142 (rendered_frame_duration - actual_frame_duration).InMicroseconds()); | |
143 const int64 frames_until_drift_exhausted = | |
144 std::ceil(max_acceptable_drift.InMicroseconds() / duration_delta); | |
145 *time_until_glitch = rendered_frame_duration * frames_until_drift_exhausted; | |
146 | |
147 if (*time_until_glitch >= minimum_time_until_glitch_) { | |
148 *cadence = clamped_cadence; | |
149 return true; | |
150 } | |
151 | |
152 return false; | |
153 } | |
154 | |
155 int VideoCadenceEstimator::GetCadenceForFrame(int index) const { | |
156 DCHECK(has_cadence()); | |
157 DCHECK_GE(index, 0); | |
158 | |
159 if (fractional_cadence_) | |
160 return index % fractional_cadence_ == 0 ? 1 : 0; | |
161 | |
162 return cadence_; | |
163 } | |
164 | |
165 } // namespace media | |
OLD | NEW |