andrew@webrtc.org | b015cbe | 2012-10-22 18:19:23 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2012 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 | |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 11 | #include <algorithm> |
| 12 | |
kjellander | 1979696 | 2017-06-30 17:45:21 | [diff] [blame] | 13 | #include "webrtc/rtc_base/rate_statistics.h" |
kwiberg | 36a2479 | 2016-10-01 05:29:43 | [diff] [blame] | 14 | #include "webrtc/test/gtest.h" |
andrew@webrtc.org | b015cbe | 2012-10-22 18:19:23 | [diff] [blame] | 15 | |
| 16 | namespace { |
| 17 | |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 18 | using webrtc::RateStatistics; |
andrew@webrtc.org | b015cbe | 2012-10-22 18:19:23 | [diff] [blame] | 19 | |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 20 | const int64_t kWindowMs = 500; |
| 21 | |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 22 | class RateStatisticsTest : public ::testing::Test { |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 23 | protected: |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 24 | RateStatisticsTest() : stats_(kWindowMs, 8000) {} |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 25 | RateStatistics stats_; |
andrew@webrtc.org | b015cbe | 2012-10-22 18:19:23 | [diff] [blame] | 26 | }; |
| 27 | |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 28 | TEST_F(RateStatisticsTest, TestStrictMode) { |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 29 | int64_t now_ms = 0; |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 30 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 31 | |
| 32 | const uint32_t kPacketSize = 1500u; |
| 33 | const uint32_t kExpectedRateBps = kPacketSize * 1000 * 8; |
| 34 | |
| 35 | // Single data point is not enough for valid estimate. |
| 36 | stats_.Update(kPacketSize, now_ms++); |
| 37 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 38 | |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 39 | // Expecting 1200 kbps since the window is initially kept small and grows as |
| 40 | // we have more data. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 41 | stats_.Update(kPacketSize, now_ms); |
| 42 | EXPECT_EQ(kExpectedRateBps, *stats_.Rate(now_ms)); |
| 43 | |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 44 | stats_.Reset(); |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 45 | // Expecting 0 after init. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 46 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 47 | |
| 48 | const int kInterval = 10; |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 49 | for (int i = 0; i < 100000; ++i) { |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 50 | if (i % kInterval == 0) |
| 51 | stats_.Update(kPacketSize, now_ms); |
| 52 | |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 53 | // Approximately 1200 kbps expected. Not exact since when packets |
| 54 | // are removed we will jump 10 ms to the next packet. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 55 | if (i > kInterval) { |
| 56 | rtc::Optional<uint32_t> rate = stats_.Rate(now_ms); |
| 57 | EXPECT_TRUE(static_cast<bool>(rate)); |
| 58 | uint32_t samples = i / kInterval + 1; |
| 59 | uint64_t total_bits = samples * kPacketSize * 8; |
| 60 | uint32_t rate_bps = static_cast<uint32_t>((1000 * total_bits) / (i + 1)); |
| 61 | EXPECT_NEAR(rate_bps, *rate, 22000u); |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 62 | } |
| 63 | now_ms += 1; |
| 64 | } |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 65 | now_ms += kWindowMs; |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 66 | // The window is 2 seconds. If nothing has been received for that time |
| 67 | // the estimate should be 0. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 68 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
andrew@webrtc.org | b015cbe | 2012-10-22 18:19:23 | [diff] [blame] | 69 | } |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 70 | |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 71 | TEST_F(RateStatisticsTest, IncreasingThenDecreasingBitrate) { |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 72 | int64_t now_ms = 0; |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 73 | stats_.Reset(); |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 74 | // Expecting 0 after init. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 75 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 76 | |
| 77 | stats_.Update(1000, ++now_ms); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 78 | const uint32_t kExpectedBitrate = 8000000; |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 79 | // 1000 bytes per millisecond until plateau is reached. |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 80 | int prev_error = kExpectedBitrate; |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 81 | rtc::Optional<uint32_t> bitrate; |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 82 | while (++now_ms < 10000) { |
| 83 | stats_.Update(1000, now_ms); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 84 | bitrate = stats_.Rate(now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 85 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 86 | int error = kExpectedBitrate - *bitrate; |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 87 | error = std::abs(error); |
| 88 | // Expect the estimation error to decrease as the window is extended. |
| 89 | EXPECT_LE(error, prev_error + 1); |
| 90 | prev_error = error; |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 91 | } |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 92 | // Window filled, expect to be close to 8000000. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 93 | EXPECT_EQ(kExpectedBitrate, *bitrate); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 94 | |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 95 | // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected. |
| 96 | while (++now_ms < 10000) { |
| 97 | stats_.Update(1000, now_ms); |
sprang@webrtc.org | 7374da3 | 2013-12-03 10:31:59 | [diff] [blame] | 98 | bitrate = stats_.Rate(now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 99 | EXPECT_EQ(kExpectedBitrate, *bitrate); |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 100 | } |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 101 | |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 102 | // Zero bytes per millisecond until 0 is reached. |
| 103 | while (++now_ms < 20000) { |
| 104 | stats_.Update(0, now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 105 | rtc::Optional<uint32_t> new_bitrate = stats_.Rate(now_ms); |
| 106 | if (static_cast<bool>(new_bitrate) && *new_bitrate != *bitrate) { |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 107 | // New bitrate must be lower than previous one. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 108 | EXPECT_LT(*new_bitrate, *bitrate); |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 109 | } else { |
| 110 | // 0 kbps expected. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 111 | EXPECT_EQ(0u, *new_bitrate); |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 112 | break; |
| 113 | } |
| 114 | bitrate = new_bitrate; |
| 115 | } |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 116 | |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 117 | // Zero bytes per millisecond until 20-second mark, 0 kbps expected. |
| 118 | while (++now_ms < 20000) { |
| 119 | stats_.Update(0, now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 120 | EXPECT_EQ(0u, *stats_.Rate(now_ms)); |
mikhal@webrtc.org | 66fba2b | 2013-11-25 17:49:28 | [diff] [blame] | 121 | } |
| 122 | } |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 123 | |
| 124 | TEST_F(RateStatisticsTest, ResetAfterSilence) { |
| 125 | int64_t now_ms = 0; |
| 126 | stats_.Reset(); |
| 127 | // Expecting 0 after init. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 128 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 129 | |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 130 | const uint32_t kExpectedBitrate = 8000000; |
| 131 | // 1000 bytes per millisecond until the window has been filled. |
| 132 | int prev_error = kExpectedBitrate; |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 133 | rtc::Optional<uint32_t> bitrate; |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 134 | while (++now_ms < 10000) { |
| 135 | stats_.Update(1000, now_ms); |
| 136 | bitrate = stats_.Rate(now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 137 | if (bitrate) { |
| 138 | int error = kExpectedBitrate - *bitrate; |
| 139 | error = std::abs(error); |
| 140 | // Expect the estimation error to decrease as the window is extended. |
| 141 | EXPECT_LE(error, prev_error + 1); |
| 142 | prev_error = error; |
| 143 | } |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 144 | } |
| 145 | // Window filled, expect to be close to 8000000. |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 146 | EXPECT_EQ(kExpectedBitrate, *bitrate); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 147 | |
| 148 | now_ms += kWindowMs + 1; |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 149 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 150 | stats_.Update(1000, now_ms); |
Erik Språng | 475c05f | 2016-06-10 20:13:21 | [diff] [blame] | 151 | ++now_ms; |
| 152 | stats_.Update(1000, now_ms); |
| 153 | // We expect two samples of 1000 bytes, and that the bitrate is measured over |
| 154 | // 500 ms, i.e. 2 * 8 * 1000 / 0.500 = 32000. |
| 155 | EXPECT_EQ(32000u, *stats_.Rate(now_ms)); |
| 156 | |
| 157 | // Reset, add the same samples again. |
| 158 | stats_.Reset(); |
| 159 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 160 | stats_.Update(1000, now_ms); |
| 161 | ++now_ms; |
| 162 | stats_.Update(1000, now_ms); |
| 163 | // We expect two samples of 1000 bytes, and that the bitrate is measured over |
| 164 | // 2 ms (window size has been reset) i.e. 2 * 8 * 1000 / 0.002 = 8000000. |
| 165 | EXPECT_EQ(kExpectedBitrate, *stats_.Rate(now_ms)); |
| 166 | } |
| 167 | |
| 168 | TEST_F(RateStatisticsTest, HandlesChangingWindowSize) { |
| 169 | int64_t now_ms = 0; |
| 170 | stats_.Reset(); |
| 171 | |
| 172 | // Sanity test window size. |
| 173 | EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms)); |
| 174 | EXPECT_FALSE(stats_.SetWindowSize(kWindowMs + 1, now_ms)); |
| 175 | EXPECT_FALSE(stats_.SetWindowSize(0, now_ms)); |
| 176 | EXPECT_TRUE(stats_.SetWindowSize(1, now_ms)); |
| 177 | EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms)); |
| 178 | |
| 179 | // Fill the buffer at a rate of 1 byte / millisecond (8 kbps). |
| 180 | const int kBatchSize = 10; |
| 181 | for (int i = 0; i <= kWindowMs; i += kBatchSize) |
| 182 | stats_.Update(kBatchSize, now_ms += kBatchSize); |
| 183 | EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms)); |
| 184 | |
| 185 | // Halve the window size, rate should stay the same. |
| 186 | EXPECT_TRUE(stats_.SetWindowSize(kWindowMs / 2, now_ms)); |
| 187 | EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms)); |
| 188 | |
| 189 | // Double the window size again, rate should stay the same. (As the window |
| 190 | // won't actually expand until new bit and bobs fall into it. |
| 191 | EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms)); |
| 192 | EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms)); |
| 193 | |
| 194 | // Fill the now empty half with bits it twice the rate. |
| 195 | for (int i = 0; i < kWindowMs / 2; i += kBatchSize) |
| 196 | stats_.Update(kBatchSize * 2, now_ms += kBatchSize); |
| 197 | |
| 198 | // Rate should have increase be 50%. |
| 199 | EXPECT_EQ(static_cast<uint32_t>((8000 * 3) / 2), *stats_.Rate(now_ms)); |
| 200 | } |
| 201 | |
| 202 | TEST_F(RateStatisticsTest, RespectsWindowSizeEdges) { |
| 203 | int64_t now_ms = 0; |
| 204 | stats_.Reset(); |
| 205 | // Expecting 0 after init. |
| 206 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 207 | |
| 208 | // One byte per ms, using one big sample. |
| 209 | stats_.Update(kWindowMs, now_ms); |
| 210 | now_ms += kWindowMs - 2; |
| 211 | // Shouldn't work! (Only one sample, not full window size.) |
| 212 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 213 | |
| 214 | // Window size should be full, and the single data point should be accepted. |
| 215 | ++now_ms; |
| 216 | rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms); |
| 217 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 218 | EXPECT_EQ(1000 * 8u, *bitrate); |
| 219 | |
| 220 | // Add another, now we have twice the bitrate. |
| 221 | stats_.Update(kWindowMs, now_ms); |
| 222 | bitrate = stats_.Rate(now_ms); |
| 223 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 224 | EXPECT_EQ(2 * 1000 * 8u, *bitrate); |
| 225 | |
| 226 | // Now that first sample should drop out... |
| 227 | now_ms += 1; |
| 228 | bitrate = stats_.Rate(now_ms); |
| 229 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 230 | EXPECT_EQ(1000 * 8u, *bitrate); |
| 231 | } |
| 232 | |
| 233 | TEST_F(RateStatisticsTest, HandlesZeroCounts) { |
| 234 | int64_t now_ms = 0; |
| 235 | stats_.Reset(); |
| 236 | // Expecting 0 after init. |
| 237 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 238 | |
| 239 | stats_.Update(kWindowMs, now_ms); |
| 240 | now_ms += kWindowMs - 1; |
| 241 | stats_.Update(0, now_ms); |
| 242 | rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms); |
| 243 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 244 | EXPECT_EQ(1000 * 8u, *bitrate); |
| 245 | |
| 246 | // Move window along so first data point falls out. |
| 247 | ++now_ms; |
| 248 | bitrate = stats_.Rate(now_ms); |
| 249 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 250 | EXPECT_EQ(0u, *bitrate); |
| 251 | |
| 252 | // Move window so last data point falls out. |
| 253 | now_ms += kWindowMs; |
| 254 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 255 | } |
| 256 | |
| 257 | TEST_F(RateStatisticsTest, HandlesQuietPeriods) { |
| 258 | int64_t now_ms = 0; |
| 259 | stats_.Reset(); |
| 260 | // Expecting 0 after init. |
| 261 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 262 | |
| 263 | stats_.Update(0, now_ms); |
| 264 | now_ms += kWindowMs - 1; |
| 265 | rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms); |
| 266 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 267 | EXPECT_EQ(0u, *bitrate); |
| 268 | |
| 269 | // Move window along so first data point falls out. |
| 270 | ++now_ms; |
| 271 | EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms))); |
| 272 | |
| 273 | // Move window a long way out. |
| 274 | now_ms += 2 * kWindowMs; |
| 275 | stats_.Update(0, now_ms); |
| 276 | bitrate = stats_.Rate(now_ms); |
| 277 | EXPECT_TRUE(static_cast<bool>(bitrate)); |
| 278 | EXPECT_EQ(0u, *bitrate); |
Stefan Holmer | 3eb722b | 2016-04-22 13:48:23 | [diff] [blame] | 279 | } |
solenberg@webrtc.org | e5117e7 | 2013-04-18 12:25:32 | [diff] [blame] | 280 | } // namespace |