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 = |
| 142 (rendered_frame_duration - actual_frame_duration) |
| 143 .magnitude() |
| 144 .InMicroseconds(); |
| 145 const int64 frames_until_drift_exhausted = |
| 146 std::ceil(max_acceptable_drift.InMicroseconds() / duration_delta); |
| 147 *time_until_glitch = rendered_frame_duration * frames_until_drift_exhausted; |
| 148 |
| 149 if (*time_until_glitch >= minimum_time_until_glitch_) { |
| 150 *cadence = clamped_cadence; |
| 151 return true; |
| 152 } |
| 153 |
| 154 return false; |
| 155 } |
| 156 |
| 157 int VideoCadenceEstimator::GetCadenceForFrame(int index) const { |
| 158 DCHECK(has_cadence()); |
| 159 DCHECK_GE(index, 0); |
| 160 |
| 161 if (fractional_cadence_) |
| 162 return index % fractional_cadence_ == 0 ? 1 : 0; |
| 163 |
| 164 return cadence_; |
| 165 } |
| 166 |
| 167 } // namespace media |
OLD | NEW |