blob: af9eeffa1986500e4c7462c94be9a0b818e386a2 [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/frequency_tracker.h"
12
13#include <cstdlib>
14#include <limits>
Florent Castelli8037fc62024-08-29 13:00:4015#include <optional>
Danil Chapovalove546ff92023-07-21 12:06:2016
Danil Chapovalove546ff92023-07-21 12:06:2017#include "api/units/frequency.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::Gt;
28using ::testing::Lt;
29
30constexpr TimeDelta kWindow = TimeDelta::Millis(500);
31constexpr TimeDelta kEpsilon = TimeDelta::Millis(1);
32
33TEST(FrequencyTrackerTest, ReturnsNulloptInitially) {
34 Timestamp now = Timestamp::Seconds(12'345);
35 FrequencyTracker stats(kWindow);
36
Florent Castelli8037fc62024-08-29 13:00:4037 EXPECT_EQ(stats.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:2038}
39
40TEST(FrequencyTrackerTest, ReturnsNulloptAfterSingleDataPoint) {
41 Timestamp now = Timestamp::Seconds(12'345);
42 FrequencyTracker stats(kWindow);
43
44 stats.Update(now);
45 now += TimeDelta::Millis(10);
46
Florent Castelli8037fc62024-08-29 13:00:4047 EXPECT_EQ(stats.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:2048}
49
50TEST(FrequencyTrackerTest, ReturnsRateAfterTwoMeasurements) {
51 Timestamp now = Timestamp::Seconds(12'345);
52 FrequencyTracker stats(kWindow);
53
54 stats.Update(now);
55 now += TimeDelta::Millis(1);
56 stats.Update(now);
57
58 // 1 event per 1 ms ~= 1'000 events per second.
59 EXPECT_EQ(stats.Rate(now), Frequency::Hertz(1'000));
60}
61
62TEST(FrequencyTrackerTest, MeasuresConstantRate) {
63 const Timestamp start = Timestamp::Seconds(12'345);
64 const TimeDelta kInterval = TimeDelta::Millis(10);
65 const Frequency kConstantRate = 1 / kInterval;
66
67 Timestamp now = start;
68 FrequencyTracker stats(kWindow);
69
70 stats.Update(now);
71 Frequency last_error = Frequency::PlusInfinity();
72 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kInterval) {
Tommi55c36002024-07-03 11:01:3573 SCOPED_TRACE(ToString(i));
Danil Chapovalove546ff92023-07-21 12:06:2074 now += kInterval;
75 stats.Update(now);
76
77 // Until window is full, rate is measured over a smaller window and might
78 // look larger than the constant rate.
Florent Castelli8037fc62024-08-29 13:00:4079 std::optional<Frequency> rate = stats.Rate(now);
Danil Chapovalove546ff92023-07-21 12:06:2080 ASSERT_GE(rate, kConstantRate);
81
82 // Expect the estimation error to decrease as the window is extended.
83 Frequency error = *rate - kConstantRate;
84 EXPECT_LE(error, last_error);
85 last_error = error;
86 }
87
88 // Once window is full, rate measurment should be stable.
89 for (TimeDelta i = TimeDelta::Zero(); i < kInterval;
90 i += TimeDelta::Millis(1)) {
Tommi55c36002024-07-03 11:01:3591 SCOPED_TRACE(ToString(i));
Danil Chapovalove546ff92023-07-21 12:06:2092 EXPECT_EQ(stats.Rate(now + i), kConstantRate);
93 }
94}
95
96TEST(FrequencyTrackerTest, CanMeasureFractionalRate) {
97 const TimeDelta kInterval = TimeDelta::Millis(134);
98 Timestamp now = Timestamp::Seconds(12'345);
99 // FrequencyTracker counts number of events in the window, thus when window is
100 // fraction of 1 second, number of events per second would always be integer.
101 const TimeDelta window = TimeDelta::Seconds(2);
102
103 FrequencyTracker framerate(window);
104 framerate.Update(now);
105 for (TimeDelta i = TimeDelta::Zero(); i < window; i += kInterval) {
106 now += kInterval;
107 framerate.Update(now);
108 }
109
110 // Should be aproximitly 7.5 fps
111 EXPECT_THAT(framerate.Rate(now),
112 AllOf(Gt(Frequency::Hertz(7)), Lt(Frequency::Hertz(8))));
113}
114
115TEST(FrequencyTrackerTest, IncreasingThenDecreasingRate) {
116 const int64_t kLargeSize = 1'500;
117 const int64_t kSmallSize = 300;
118 const TimeDelta kLargeInterval = TimeDelta::Millis(10);
119 const TimeDelta kSmallInterval = TimeDelta::Millis(2);
120
121 Timestamp now = Timestamp::Seconds(12'345);
122 FrequencyTracker stats(kWindow);
123
124 stats.Update(kLargeSize, now);
125 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kLargeInterval) {
Tommi55c36002024-07-03 11:01:35126 SCOPED_TRACE(ToString(i));
Danil Chapovalove546ff92023-07-21 12:06:20127 now += kLargeInterval;
128 stats.Update(kLargeSize, now);
129 }
Florent Castelli8037fc62024-08-29 13:00:40130 std::optional<Frequency> last_rate = stats.Rate(now);
Danil Chapovalove546ff92023-07-21 12:06:20131 EXPECT_EQ(last_rate, kLargeSize / kLargeInterval);
132
133 // Decrease rate with smaller measurments.
134 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kLargeInterval) {
Tommi55c36002024-07-03 11:01:35135 SCOPED_TRACE(ToString(i));
Danil Chapovalove546ff92023-07-21 12:06:20136 now += kLargeInterval;
137 stats.Update(kSmallSize, now);
138
Florent Castelli8037fc62024-08-29 13:00:40139 std::optional<Frequency> rate = stats.Rate(now);
Danil Chapovalove546ff92023-07-21 12:06:20140 EXPECT_LT(rate, last_rate);
141
142 last_rate = rate;
143 }
144 EXPECT_EQ(last_rate, kSmallSize / kLargeInterval);
145
146 // Increase rate with more frequent measurments.
147 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kSmallInterval) {
Tommi55c36002024-07-03 11:01:35148 SCOPED_TRACE(ToString(i));
Danil Chapovalove546ff92023-07-21 12:06:20149 now += kSmallInterval;
150 stats.Update(kSmallSize, now);
151
Florent Castelli8037fc62024-08-29 13:00:40152 std::optional<Frequency> rate = stats.Rate(now);
Danil Chapovalove546ff92023-07-21 12:06:20153 EXPECT_GE(rate, last_rate);
154
155 last_rate = rate;
156 }
157 EXPECT_EQ(last_rate, kSmallSize / kSmallInterval);
158}
159
160TEST(FrequencyTrackerTest, ResetAfterSilence) {
161 const TimeDelta kInterval = TimeDelta::Millis(10);
162 const int64_t kPixels = 640 * 360;
163
164 Timestamp now = Timestamp::Seconds(12'345);
165 FrequencyTracker pixel_rate(kWindow);
166
167 // Feed data until window has been filled.
168 pixel_rate.Update(kPixels, now);
169 for (TimeDelta i = TimeDelta::Zero(); i < kWindow; i += kInterval) {
170 now += kInterval;
171 pixel_rate.Update(kPixels, now);
172 }
173 ASSERT_GT(pixel_rate.Rate(now), Frequency::Zero());
174
175 now += kWindow + kEpsilon;
176 // Silence over window size should trigger auto reset for coming sample.
Florent Castelli8037fc62024-08-29 13:00:40177 EXPECT_EQ(pixel_rate.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:20178 pixel_rate.Update(kPixels, now);
179 // Single measurment after reset is not enough to estimate the rate.
Florent Castelli8037fc62024-08-29 13:00:40180 EXPECT_EQ(pixel_rate.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:20181
182 // Manual reset, add the same check again.
183 pixel_rate.Reset();
Florent Castelli8037fc62024-08-29 13:00:40184 EXPECT_EQ(pixel_rate.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:20185 now += kInterval;
186 pixel_rate.Update(kPixels, now);
Florent Castelli8037fc62024-08-29 13:00:40187 EXPECT_EQ(pixel_rate.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:20188}
189
190TEST(FrequencyTrackerTest, ReturnsNulloptWhenOverflows) {
191 Timestamp now = Timestamp::Seconds(12'345);
192 FrequencyTracker stats(kWindow);
193
194 int64_t very_large_number = std::numeric_limits<int64_t>::max();
195 stats.Update(very_large_number, now);
196 now += kEpsilon;
197 stats.Update(very_large_number, now);
198
Florent Castelli8037fc62024-08-29 13:00:40199 EXPECT_EQ(stats.Rate(now), std::nullopt);
Danil Chapovalove546ff92023-07-21 12:06:20200}
201
202} // namespace
203} // namespace webrtc