| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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_CAPTURE_CONTENT_FEEDBACK_SIGNAL_ACCUMULATOR_H_ | |
| 6 #define MEDIA_CAPTURE_CONTENT_FEEDBACK_SIGNAL_ACCUMULATOR_H_ | |
| 7 | |
| 8 #include "base/time/time.h" | |
| 9 #include "media/capture/capture_export.h" | |
| 10 | |
| 11 namespace media { | |
| 12 | |
| 13 // Utility class for maintaining an exponentially-decaying average of feedback | |
| 14 // signal values whose updates occur at undetermined, possibly irregular time | |
| 15 // intervals. | |
| 16 // | |
| 17 // Feedback signals can be made by multiple sources. Meaning, there can be | |
| 18 // several values provided for the same timestamp. In this case, the greatest | |
| 19 // value is retained and used to re-compute the average. Therefore, the values | |
| 20 // provided to this class' methods should be appropriately translated with this | |
| 21 // in mind. For example, an "fraction available" metric should be translated | |
| 22 // into a "fraction utilized" one. | |
| 23 // | |
| 24 // Usage note: Reset() must be called at least once before the first call to | |
| 25 // Update(). | |
| 26 // | |
| 27 // This template class supports data points that are timestamped using either | |
| 28 // |base::TimeDelta| or |base::TimeTicks|. | |
| 29 template <typename TimeType> | |
| 30 class FeedbackSignalAccumulator { | |
| 31 public: | |
| 32 // |half_life| is the amount of time that must pass between two data points to | |
| 33 // move the accumulated average value halfway in-between. Example: If | |
| 34 // |half_life| is one second, then calling Reset(0.0, t=0s) and then | |
| 35 // Update(1.0, t=1s) will result in an accumulated average value of 0.5. | |
| 36 explicit FeedbackSignalAccumulator(base::TimeDelta half_life) | |
| 37 : half_life_(half_life), average_(NAN) { | |
| 38 DCHECK(half_life_ > base::TimeDelta()); | |
| 39 } | |
| 40 | |
| 41 // Erase all memory of historical values, re-starting with the given | |
| 42 // |starting_value|. | |
| 43 void Reset(double starting_value, TimeType timestamp) { | |
| 44 average_ = update_value_ = prior_average_ = starting_value; | |
| 45 reset_time_ = update_time_ = prior_update_time_ = timestamp; | |
| 46 } | |
| 47 | |
| 48 TimeType reset_time() const { return reset_time_; } | |
| 49 | |
| 50 // Apply the given |value|, which was observed at the given |timestamp|, to | |
| 51 // the accumulated average. If the timestamp is in chronological order, the | |
| 52 // update succeeds and this method returns true. Otherwise the update has no | |
| 53 // effect and false is returned. If there are two or more updates at the same | |
| 54 // |timestamp|, only the one with the greatest value will be accounted for | |
| 55 // (see class comments for elaboration). | |
| 56 bool Update(double value, TimeType timestamp) { | |
| 57 DCHECK(!std::isnan(average_)) << "Reset() must be called once."; | |
| 58 | |
| 59 if (timestamp < update_time_) { | |
| 60 return false; // Not in chronological order. | |
| 61 } else if (timestamp == update_time_) { | |
| 62 if (timestamp == reset_time_) { | |
| 63 // Edge case: Multiple updates at reset timestamp. | |
| 64 average_ = update_value_ = prior_average_ = | |
| 65 std::max(value, update_value_); | |
| 66 return true; | |
| 67 } | |
| 68 if (value <= update_value_) | |
| 69 return true; | |
| 70 update_value_ = value; | |
| 71 } else { | |
| 72 prior_average_ = average_; | |
| 73 prior_update_time_ = update_time_; | |
| 74 update_value_ = value; | |
| 75 update_time_ = timestamp; | |
| 76 } | |
| 77 | |
| 78 const double elapsed_us = static_cast<double>( | |
| 79 (update_time_ - prior_update_time_).InMicroseconds()); | |
| 80 const double weight = | |
| 81 elapsed_us / (elapsed_us + half_life_.InMicroseconds()); | |
| 82 average_ = weight * update_value_ + (1.0 - weight) * prior_average_; | |
| 83 DCHECK(std::isfinite(average_)); | |
| 84 | |
| 85 return true; | |
| 86 } | |
| 87 | |
| 88 TimeType update_time() const { return update_time_; } | |
| 89 | |
| 90 // Returns the current accumulated average value. | |
| 91 double current() const { return average_; } | |
| 92 | |
| 93 private: | |
| 94 // In conjunction with the |update_time_| and |prior_update_time_|, this is | |
| 95 // used to compute the weight of the current update value versus the prior | |
| 96 // accumulated average. | |
| 97 const base::TimeDelta half_life_; | |
| 98 | |
| 99 TimeType reset_time_; // |timestamp| passed in last call to Reset(). | |
| 100 double average_; // Current accumulated average. | |
| 101 double update_value_; // Latest |value| accepted by Update(). | |
| 102 TimeType update_time_; // Latest |timestamp| accepted by Update(). | |
| 103 double prior_average_; // Accumulated average before last call to Update(). | |
| 104 TimeType prior_update_time_; // |timestamp| in prior call to Update(). | |
| 105 }; | |
| 106 | |
| 107 } // namespace media | |
| 108 | |
| 109 #endif // MEDIA_CAPTURE_CONTENT_FEEDBACK_SIGNAL_ACCUMULATOR_H_ | |
| OLD | NEW |