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 |