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 #ifndef MEDIA_FILTERS_VIDEO_CADENCE_ESTIMATOR_H_ |
| 6 #define MEDIA_FILTERS_VIDEO_CADENCE_ESTIMATOR_H_ |
| 7 |
| 8 #include "base/time/time.h" |
| 9 #include "media/base/media_export.h" |
| 10 |
| 11 namespace media { |
| 12 |
| 13 // Estimates whether a given frame duration and render interval length have a |
| 14 // render cadence which would allow for optimal uniformity of displayed frame |
| 15 // durations over time. |
| 16 // |
| 17 // Cadence is the ratio of the frame duration to render interval length. I.e. |
| 18 // for 30fps in 60Hz the cadence would be (1/30) / (1/60) = 60 / 30 = 2. It's |
| 19 // common that this is not an exact integer, e.g., 29.974fps in 60Hz which |
| 20 // would have a cadence of (1/29.974) / (1/60) = ~2.0029. |
| 21 // |
| 22 // Clamped integer cadence means we round the actual cadence (~2.0029 in the |
| 23 // pending example) to the nearest integer value (2 in this case). If the |
| 24 // delta between those values is small, we can choose to render frames for the |
| 25 // integer number of render intervals; shortening or lengthening the actual |
| 26 // rendered frame duration. Doing so ensures each frame gets an optimal amount |
| 27 // of display time. |
| 28 // |
| 29 // Obviously clamping cadence like that leads to drift over time of the actual |
| 30 // VideoFrame timestamp relative to its rendered time, so we perform some |
| 31 // calculations to ensure we only clamp cadence when it will take some time to |
| 32 // drift an undesirable amount; see CalculateCadence() for details on how this |
| 33 // calculation is made. |
| 34 // |
| 35 // Notably, this concept can be extended to include fractional cadence when the |
| 36 // frame duration is shorter than the render interval; e.g. 120fps in 60Hz. In |
| 37 // this case, the first frame in each group of N frames is displayed once, while |
| 38 // the next N - 1 frames are dropped; where N is the fractional cadence of the |
| 39 // frame group. Using the pending example N = 120/60 = 2. See implementations |
| 40 // of CalculateCadence() and UpdateCadenceEstimate() for more details. |
| 41 // |
| 42 // In practice this works out to the following for common setups if we use |
| 43 // cadence based selection: |
| 44 // |
| 45 // 29.5fps in 60Hz, ~17ms max drift => exhausted in ~1 second. |
| 46 // 29.9fps in 60Hz, ~17ms max drift => exhausted in ~16.4 seconds. |
| 47 // 24fps in 60Hz, ~21ms max drift => exhausted in ~0.15 seconds. |
| 48 // 25fps in 60Hz, 20ms max drift => exhausted in ~4.0 seconds. |
| 49 // 59.9fps in 60Hz, ~8.3ms max drift => exhausted in ~8.2 seconds. |
| 50 // 24.9fps in 50Hz, ~20ms max drift => exhausted in ~20.5 seconds. |
| 51 // 120fps in 59.9Hz, ~8.3ms max drift => exhausted in ~8.2 seconds. |
| 52 // |
| 53 class MEDIA_EXPORT VideoCadenceEstimator { |
| 54 public: |
| 55 // As mentioned in the introduction, the determination of whether to clamp to |
| 56 // a given cadence is based on how long it takes before a frame would have to |
| 57 // be dropped or repeated to compensate for reaching the maximum acceptable |
| 58 // drift; this time length is controlled by |minimum_time_until_glitch|. |
| 59 explicit VideoCadenceEstimator(base::TimeDelta minimum_time_until_glitch); |
| 60 ~VideoCadenceEstimator(); |
| 61 |
| 62 // Clears stored cadence information. |
| 63 void Reset(); |
| 64 |
| 65 // Updates the estimates for |cadence_| and |fractional_cadence_| based on the |
| 66 // given values as described in the introduction above. |
| 67 // |
| 68 // Clients should call this and then update the cadence for all frames via the |
| 69 // GetCadenceForFrame() method. |
| 70 // |
| 71 // Cadence changes will not take affect until enough render intervals have |
| 72 // elapsed. For the purposes of hysteresis, each UpdateCadenceEstimate() call |
| 73 // is assumed to elapse one |render_interval| worth of time. |
| 74 // |
| 75 // Returns true if the cadence has changed since the last call. |
| 76 bool UpdateCadenceEstimate(base::TimeDelta render_interval, |
| 77 base::TimeDelta frame_duration, |
| 78 base::TimeDelta max_acceptable_drift); |
| 79 |
| 80 // Returns true if a useful cadence was found. |
| 81 bool has_cadence() const { return cadence_ > 0; } |
| 82 |
| 83 // Given a frame |index|, where zero is the most recently rendered frame, |
| 84 // returns the ideal cadence for that frame. |
| 85 int GetCadenceForFrame(int index) const; |
| 86 |
| 87 void set_cadence_hysteresis_threshold_for_testing(base::TimeDelta threshold) { |
| 88 cadence_hysteresis_threshold_ = threshold; |
| 89 } |
| 90 |
| 91 int get_cadence_for_testing() const { |
| 92 return cadence_ && fractional_cadence_ ? fractional_cadence_ : cadence_; |
| 93 } |
| 94 |
| 95 private: |
| 96 // Calculates the clamped cadence for the given |render_interval| and |
| 97 // |frame_duration|, then calculates how long that cadence can be used before |
| 98 // exhausting |max_acceptable_drift|. If the time until exhaustion is greater |
| 99 // than |minimum_time_until_glitch_|, returns true and sets |cadence| to the |
| 100 // clamped cadence. If the clamped cadence is unusable, |cadence| will be set |
| 101 // to zero. |
| 102 // |
| 103 // If |fractional| is true, GetCadence() will calculate the clamped cadence |
| 104 // using the ratio of the |render_interval| to |frame_duration| instead of |
| 105 // vice versa. |
| 106 // |
| 107 // Sets |time_until_glitch| to the computed glitch time. Set to zero if the |
| 108 // clamped cadence is unusable. |
| 109 bool CalculateCadence(base::TimeDelta render_interval, |
| 110 base::TimeDelta frame_duration, |
| 111 base::TimeDelta max_acceptable_drift, |
| 112 bool fractional, |
| 113 int* cadence, |
| 114 base::TimeDelta* time_until_glitch); |
| 115 |
| 116 // The idealized cadence for all frames seen thus far; updated based upon the |
| 117 // ratio of |frame_duration| to |render_interval|, or vice versa, as given to |
| 118 // UpdateCadenceEstimate(). Zero if no integer cadence could be detected. |
| 119 // |
| 120 // Fractional cadences are handled by strongly preferring the first frame in |
| 121 // a series if it fits within acceptable drift. E.g., with 120fps content on |
| 122 // a 60Hz monitor we'll strongly prefer the first frame of every 2 frames. |
| 123 // |
| 124 // |fractional_cadence_| is the number of frames per render interval; the |
| 125 // first of which would be rendered and the rest dropped. |
| 126 int cadence_; |
| 127 int fractional_cadence_; |
| 128 |
| 129 // Used as hysteresis to prevent oscillation between cadence and coverage |
| 130 // based rendering methods. Pending values are updated upon each new cadence |
| 131 // detected by UpdateCadenceEstimate(). |
| 132 // |
| 133 // Once a new cadence is detected, |render_intervals_cadence_held_| is |
| 134 // incremented for each UpdateCadenceEstimate() call where the cadence matches |
| 135 // one of the pending values. |render_intervals_cadence_held_| is cleared |
| 136 // when a "new" cadence matches |cadence_| or |pending_cadence_|. |
| 137 // |
| 138 // Once |kMinimumCadenceDurationMs| is exceeded in render intervals, the |
| 139 // detected cadence is set in |cadence_| and |fractional_cadence_|. |
| 140 int pending_cadence_; |
| 141 int pending_fractional_cadence_; |
| 142 int render_intervals_cadence_held_; |
| 143 base::TimeDelta cadence_hysteresis_threshold_; |
| 144 |
| 145 // The minimum amount of time allowed before a glitch occurs before confirming |
| 146 // cadence for a given render interval and frame duration. |
| 147 const base::TimeDelta minimum_time_until_glitch_; |
| 148 |
| 149 DISALLOW_COPY_AND_ASSIGN(VideoCadenceEstimator); |
| 150 }; |
| 151 |
| 152 } // namespace media |
| 153 |
| 154 #endif // MEDIA_FILTERS_VIDEO_CADENCE_ESTIMATOR_H_ |
OLD | NEW |