blob: 2129aebfdd2d6a0f158a86ccbcdd593d78b0ed5d [file] [log] [blame]
Danil Chapovalove546ff92023-07-21 12:06:201/*
2 * Copyright (c) 2023 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 "rtc_base/bitrate_tracker.h"
12
13#include <cstdlib>
14#include <limits>
15
16#include "absl/types/optional.h"
17#include "api/units/data_rate.h"
18#include "api/units/time_delta.h"
19#include "api/units/timestamp.h"
20#include "test/gmock.h"
21#include "test/gtest.h"
22
23namespace webrtc {
24namespace {
25
26using ::testing::AllOf;
27using ::testing::Ge;
28using ::testing::Le;
29
30constexpr TimeDelta kWindow = TimeDelta::Millis(500);
31constexpr TimeDelta kEpsilon = TimeDelta::Millis(1);
32
33TEST(BitrateTrackerTest, ReturnsNulloptInitially) {
34 Timestamp now = Timestamp::Seconds(12'345);
35 BitrateTracker stats(kWindow);
36
37 EXPECT_EQ(stats.Rate(now), absl::nullopt);
38}
39
40TEST(BitrateTrackerTest, ReturnsNulloptAfterSingleDataPoint) {
41 Timestamp now = Timestamp::Seconds(12'345);
42 BitrateTracker stats(kWindow);
43
44 stats.Update(1'500, now);
45 now += TimeDelta::Millis(10);
46
47 EXPECT_EQ(stats.Rate(now), absl::nullopt);
48}
49
50TEST(BitrateTrackerTest, ReturnsRateAfterTwoMeasurements) {
51 Timestamp now = Timestamp::Seconds(12'345);
52 BitrateTracker stats(kWindow);
53
54 stats.Update(1'500, now);
55 now += TimeDelta::Millis(10);
56 stats.Update(1'500, now);
57
58 // One packet every 10ms would result in 1.2 Mbps, but until window is full,
59 // it could be treated as two packets in ~10ms window, measuring twice that
60 // bitrate.
61 EXPECT_THAT(stats.Rate(now), AllOf(Ge(DataRate::BitsPerSec(1'200'000)),
62 Le(DataRate::BitsPerSec(2'400'000))));
63}
64
65TEST(BitrateTrackerTest, MeasuresConstantRate) {
66 const Timestamp start = Timestamp::Seconds(12'345);
67 const TimeDelta kInterval = TimeDelta::Millis(10);
68 const DataSize kPacketSize = DataSize::Bytes(1'500);
69 const DataRate kConstantRate = kPacketSize / kInterval;
70
71 Timestamp now = start;
72 BitrateTracker stats(kWindow);
73
74 stats.Update(kPacketSize, now);
75 DataSize total_size = kPacketSize;
76 DataRate last_error = DataRate::PlusInfinity();
77 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kInterval) {
78 SCOPED_TRACE(i);
79 now += kInterval;
80 total_size += kPacketSize;
81 stats.Update(kPacketSize, now);
82
83 // Until window is full, bitrate is measured over a smaller window and might
84 // look larger than the constant rate.
85 absl::optional<DataRate> bitrate = stats.Rate(now);
86 ASSERT_THAT(bitrate,
87 AllOf(Ge(kConstantRate), Le(total_size / (now - start))));
88
89 // Expect the estimation error to decrease as the window is extended.
90 DataRate error = *bitrate - kConstantRate;
91 EXPECT_LE(error, last_error);
92 last_error = error;
93 }
94
95 // Once window is full, bitrate measurment should be stable.
96 for (TimeDelta i = TimeDelta::Zero(); i < kInterval;
97 i += TimeDelta::Millis(1)) {
98 SCOPED_TRACE(i);
99 EXPECT_EQ(stats.Rate(now + i), kConstantRate);
100 }
101}
102
103TEST(BitrateTrackerTest, IncreasingThenDecreasingBitrate) {
104 const DataSize kLargePacketSize = DataSize::Bytes(1'500);
105 const DataSize kSmallPacketSize = DataSize::Bytes(300);
106 const TimeDelta kLargeInterval = TimeDelta::Millis(10);
107 const TimeDelta kSmallInterval = TimeDelta::Millis(2);
108
109 Timestamp now = Timestamp::Seconds(12'345);
110 BitrateTracker stats(kWindow);
111
112 stats.Update(kLargePacketSize, now);
113 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kLargeInterval) {
114 SCOPED_TRACE(i);
115 now += kLargeInterval;
116 stats.Update(kLargePacketSize, now);
117 }
118 absl::optional<DataRate> last_bitrate = stats.Rate(now);
119 EXPECT_EQ(last_bitrate, kLargePacketSize / kLargeInterval);
120
121 // Decrease bitrate with smaller measurments.
122 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kLargeInterval) {
123 SCOPED_TRACE(i);
124 now += kLargeInterval;
125 stats.Update(kSmallPacketSize, now);
126
127 absl::optional<DataRate> bitrate = stats.Rate(now);
128 EXPECT_LT(bitrate, last_bitrate);
129
130 last_bitrate = bitrate;
131 }
132 EXPECT_EQ(last_bitrate, kSmallPacketSize / kLargeInterval);
133
134 // Increase bitrate with more frequent measurments.
135 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kSmallInterval) {
136 SCOPED_TRACE(i);
137 now += kSmallInterval;
138 stats.Update(kSmallPacketSize, now);
139
140 absl::optional<DataRate> bitrate = stats.Rate(now);
141 EXPECT_GE(bitrate, last_bitrate);
142
143 last_bitrate = bitrate;
144 }
145 EXPECT_EQ(last_bitrate, kSmallPacketSize / kSmallInterval);
146}
147
148TEST(BitrateTrackerTest, ResetAfterSilence) {
149 const TimeDelta kInterval = TimeDelta::Millis(10);
150 const DataSize kPacketSize = DataSize::Bytes(1'500);
151
152 Timestamp now = Timestamp::Seconds(12'345);
153 BitrateTracker stats(kWindow);
154
155 // Feed data until window has been filled.
156 stats.Update(kPacketSize, now);
157 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kInterval) {
158 now += kInterval;
159 stats.Update(kPacketSize, now);
160 }
161 ASSERT_GT(stats.Rate(now), DataRate::Zero());
162
163 now += kWindow + kEpsilon;
164 // Silence over window size should trigger auto reset for coming sample.
165 EXPECT_EQ(stats.Rate(now), absl::nullopt);
166 stats.Update(kPacketSize, now);
167 // Single measurment after reset is not enough to estimate the rate.
168 EXPECT_EQ(stats.Rate(now), absl::nullopt);
169
170 // Manual reset, add the same check again.
171 stats.Reset();
172 EXPECT_EQ(stats.Rate(now), absl::nullopt);
173 now += kInterval;
174 stats.Update(kPacketSize, now);
175 EXPECT_EQ(stats.Rate(now), absl::nullopt);
176}
177
178TEST(BitrateTrackerTest, HandlesChangingWindowSize) {
179 Timestamp now = Timestamp::Seconds(12'345);
180 BitrateTracker stats(kWindow);
181
182 // Check window size is validated.
183 EXPECT_TRUE(stats.SetWindowSize(kWindow, now));
184 EXPECT_FALSE(stats.SetWindowSize(kWindow + kEpsilon, now));
185 EXPECT_FALSE(stats.SetWindowSize(TimeDelta::Zero(), now));
186 EXPECT_TRUE(stats.SetWindowSize(kEpsilon, now));
187 EXPECT_TRUE(stats.SetWindowSize(kWindow, now));
188
189 // Fill the buffer at a rate of 10 bytes per 10 ms (8 kbps).
190 const DataSize kValue = DataSize::Bytes(10);
191 const TimeDelta kInterval = TimeDelta::Millis(10);
192 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kInterval) {
193 now += kInterval;
194 stats.Update(kValue, now);
195 }
196 ASSERT_GT(stats.Rate(now), DataRate::BitsPerSec(8'000));
197
198 // Halve the window size, rate should stay the same.
199 EXPECT_TRUE(stats.SetWindowSize(kWindow / 2, now));
200 EXPECT_EQ(stats.Rate(now), DataRate::BitsPerSec(8'000));
201
202 // Double the window size again, rate should stay the same.
203 // The window won't actually expand until new calls to the `Update`.
204 EXPECT_TRUE(stats.SetWindowSize(kWindow, now));
205 EXPECT_EQ(stats.Rate(now), DataRate::BitsPerSec(8'000));
206
207 // Fill the now empty window half at twice the rate.
208 for (TimeDelta i = TimeDelta::Zero(); i < kWindow / 2; i += kInterval) {
209 now += kInterval;
210 stats.Update(2 * kValue, now);
211 }
212
213 // Rate should have increased by 50%.
214 EXPECT_EQ(stats.Rate(now), DataRate::BitsPerSec(12'000));
215}
216
217TEST(BitrateTrackerTest, HandlesZeroCounts) {
218 const DataSize kPacketSize = DataSize::Bytes(1'500);
219 const TimeDelta kInterval = TimeDelta::Millis(10);
220 const Timestamp start = Timestamp::Seconds(12'345);
221
222 Timestamp now = start;
223 BitrateTracker stats(kWindow);
224
225 stats.Update(kPacketSize, now);
226 ASSERT_EQ(stats.Rate(now), absl::nullopt);
227 now += kInterval;
228 stats.Update(0, now);
229 absl::optional<DataRate> last_bitrate = stats.Rate(now);
230 EXPECT_GT(last_bitrate, DataRate::Zero());
231 now += kInterval;
232 while (now < start + kWindow) {
233 SCOPED_TRACE(now - start);
234 stats.Update(0, now);
235
236 absl::optional<DataRate> bitrate = stats.Rate(now);
237 EXPECT_GT(bitrate, DataRate::Zero());
238 // As window expands, average bitrate decreases.
239 EXPECT_LT(bitrate, last_bitrate);
240
241 last_bitrate = bitrate;
242 now += kInterval;
243 }
244
245 // Initial kPacketSize should be outside the window now, so overall bitrate
246 // should be zero
247 EXPECT_EQ(stats.Rate(now), DataRate::Zero());
248
249 // Single measurment should be enough to get non zero rate.
250 stats.Update(kPacketSize, now);
251 EXPECT_EQ(stats.Rate(now), kPacketSize / kWindow);
252}
253
254TEST(BitrateTrackerTest, ReturnsNulloptWhenOverflows) {
255 Timestamp now = Timestamp::Seconds(12'345);
256 BitrateTracker stats(kWindow);
257
258 int64_t very_large_number = std::numeric_limits<int64_t>::max();
259 stats.Update(very_large_number, now);
260 now += kEpsilon;
261 stats.Update(very_large_number, now);
262
263 EXPECT_EQ(stats.Rate(now), absl::nullopt);
264}
265
266} // namespace
267} // namespace webrtc