blob: b8e4e52a8bde7f66043a00a0174e8b8fc73d46ee [file] [log] [blame]
Jakob Ivarsson1eb3d7e2019-02-21 15:42:31 +01001/*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <algorithm>
12#include <numeric>
13
14#include "modules/audio_coding/neteq/histogram.h"
15#include "rtc_base/checks.h"
16#include "rtc_base/numerics/safe_conversions.h"
17
18namespace webrtc {
19
20Histogram::Histogram(size_t num_buckets, int forget_factor)
21 : buckets_(num_buckets, 0),
22 forget_factor_(0),
23 base_forget_factor_(forget_factor) {}
24
25Histogram::~Histogram() {}
26
27// Each element in the vector is first multiplied by the forgetting factor
28// |forget_factor_|. Then the vector element indicated by |iat_packets| is then
29// increased (additive) by 1 - |forget_factor_|. This way, the probability of
30// |iat_packets| is slightly increased, while the sum of the histogram remains
31// constant (=1).
32// Due to inaccuracies in the fixed-point arithmetic, the histogram may no
33// longer sum up to 1 (in Q30) after the update. To correct this, a correction
34// term is added or subtracted from the first element (or elements) of the
35// vector.
36// The forgetting factor |forget_factor_| is also updated. When the DelayManager
37// is reset, the factor is set to 0 to facilitate rapid convergence in the
38// beginning. With each update of the histogram, the factor is increased towards
39// the steady-state value |kIatFactor_|.
40void Histogram::Add(int value) {
41 RTC_DCHECK(value >= 0);
42 RTC_DCHECK(value < static_cast<int>(buckets_.size()));
43 int vector_sum = 0; // Sum up the vector elements as they are processed.
44 // Multiply each element in |buckets_| with |forget_factor_|.
45 for (int& bucket : buckets_) {
46 bucket = (static_cast<int64_t>(bucket) * forget_factor_) >> 15;
47 vector_sum += bucket;
48 }
49
50 // Increase the probability for the currently observed inter-arrival time
51 // by 1 - |forget_factor_|. The factor is in Q15, |buckets_| in Q30.
52 // Thus, left-shift 15 steps to obtain result in Q30.
53 buckets_[value] += (32768 - forget_factor_) << 15;
54 vector_sum += (32768 - forget_factor_) << 15; // Add to vector sum.
55
56 // |buckets_| should sum up to 1 (in Q30), but it may not due to
57 // fixed-point rounding errors.
58 vector_sum -= 1 << 30; // Should be zero. Compensate if not.
59 if (vector_sum != 0) {
60 // Modify a few values early in |buckets_|.
61 int flip_sign = vector_sum > 0 ? -1 : 1;
62 for (int& bucket : buckets_) {
63 // Add/subtract 1/16 of the element, but not more than |vector_sum|.
64 int correction = flip_sign * std::min(abs(vector_sum), bucket >> 4);
65 bucket += correction;
66 vector_sum += correction;
67 if (abs(vector_sum) == 0) {
68 break;
69 }
70 }
71 }
72 RTC_DCHECK(vector_sum == 0); // Verify that the above is correct.
73
74 // Update |forget_factor_| (changes only during the first seconds after a
75 // reset). The factor converges to |base_forget_factor_|.
76 forget_factor_ += (base_forget_factor_ - forget_factor_ + 3) >> 2;
77}
78
79int Histogram::Quantile(int probability) {
80 // Find the bucket for which the probability of observing an
81 // inter-arrival time larger than or equal to |index| is larger than or
82 // equal to |probability|. The sought probability is estimated using
83 // the histogram as the reverse cumulant PDF, i.e., the sum of elements from
84 // the end up until |index|. Now, since the sum of all elements is 1
85 // (in Q30) by definition, and since the solution is often a low value for
86 // |iat_index|, it is more efficient to start with |sum| = 1 and subtract
87 // elements from the start of the histogram.
88 int inverse_probability = (1 << 30) - probability;
89 size_t index = 0; // Start from the beginning of |buckets_|.
90 int sum = 1 << 30; // Assign to 1 in Q30.
91 sum -= buckets_[index]; // Ensure that target level is >= 1.
92
93 do {
94 // Subtract the probabilities one by one until the sum is no longer greater
95 // than |inverse_probability|.
96 ++index;
97 sum -= buckets_[index];
98 } while ((sum > inverse_probability) && (index < buckets_.size() - 1));
99 return static_cast<int>(index);
100}
101
102// Set the histogram vector to an exponentially decaying distribution
103// buckets_[i] = 0.5^(i+1), i = 0, 1, 2, ...
104// buckets_ is in Q30.
105void Histogram::Reset() {
106 // Set temp_prob to (slightly more than) 1 in Q14. This ensures that the sum
107 // of buckets_ is 1.
108 uint16_t temp_prob = 0x4002; // 16384 + 2 = 100000000000010 binary.
109 for (int& bucket : buckets_) {
110 temp_prob >>= 1;
111 bucket = temp_prob << 16;
112 }
113 forget_factor_ = 0; // Adapt the histogram faster for the first few packets.
114}
115
116int Histogram::NumBuckets() const {
117 return buckets_.size();
118}
119
120void Histogram::Scale(int old_bucket_width, int new_bucket_width) {
121 buckets_ = ScaleBuckets(buckets_, old_bucket_width, new_bucket_width);
122}
123
124std::vector<int> Histogram::ScaleBuckets(const std::vector<int>& buckets,
125 int old_bucket_width,
126 int new_bucket_width) {
127 RTC_DCHECK_GT(old_bucket_width, 0);
128 RTC_DCHECK_GT(new_bucket_width, 0);
129 RTC_DCHECK_EQ(old_bucket_width % 10, 0);
130 RTC_DCHECK_EQ(new_bucket_width % 10, 0);
131 std::vector<int> new_histogram(buckets.size(), 0);
132 int64_t acc = 0;
133 int time_counter = 0;
134 size_t new_histogram_idx = 0;
135 for (size_t i = 0; i < buckets.size(); i++) {
136 acc += buckets[i];
137 time_counter += old_bucket_width;
138 // The bins should be scaled, to ensure the histogram still sums to one.
139 const int64_t scaled_acc = acc * new_bucket_width / time_counter;
140 int64_t actually_used_acc = 0;
141 while (time_counter >= new_bucket_width) {
142 const int64_t old_histogram_val = new_histogram[new_histogram_idx];
143 new_histogram[new_histogram_idx] =
144 rtc::saturated_cast<int>(old_histogram_val + scaled_acc);
145 actually_used_acc += new_histogram[new_histogram_idx] - old_histogram_val;
146 new_histogram_idx =
147 std::min(new_histogram_idx + 1, new_histogram.size() - 1);
148 time_counter -= new_bucket_width;
149 }
150 // Only subtract the part that was succesfully written to the new histogram.
151 acc -= actually_used_acc;
152 }
153 // If there is anything left in acc (due to rounding errors), add it to the
154 // last bin. If we cannot add everything to the last bin we need to add as
155 // much as possible to the bins after the last bin (this is only possible
156 // when compressing a histogram).
157 while (acc > 0 && new_histogram_idx < new_histogram.size()) {
158 const int64_t old_histogram_val = new_histogram[new_histogram_idx];
159 new_histogram[new_histogram_idx] =
160 rtc::saturated_cast<int>(old_histogram_val + acc);
161 acc -= new_histogram[new_histogram_idx] - old_histogram_val;
162 new_histogram_idx++;
163 }
164 RTC_DCHECK_EQ(buckets.size(), new_histogram.size());
165 if (acc == 0) {
166 // If acc is non-zero, we were not able to add everything to the new
167 // histogram, so this check will not hold.
168 RTC_DCHECK_EQ(accumulate(buckets.begin(), buckets.end(), 0ll),
169 accumulate(new_histogram.begin(), new_histogram.end(), 0ll));
170 }
171 return new_histogram;
172}
173
174} // namespace webrtc