TestDriftController.cpp (15576B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "DriftController.h" 7 #include "gtest/gtest.h" 8 9 using namespace mozilla; 10 using TimeUnit = media::TimeUnit; 11 12 // Advance the output by the specified duration, using a calculated input 13 // packet duration that provides the specified buffering level. 14 void AdvanceByOutputDuration(TimeUnit* aCurrentBuffered, 15 DriftController* aController, 16 TimeUnit aOutputDuration, 17 uint32_t aNextBufferedInputFrames) { 18 uint32_t nominalSourceRate = aController->mSourceRate; 19 uint32_t nominalTargetRate = aController->mTargetRate; 20 uint32_t correctedRate = aController->GetCorrectedSourceRate(); 21 // Use a denominator to exactly track (1/nominalTargetRate)ths of 22 // durations in seconds of input frames buffered in the resampler. 23 *aCurrentBuffered = aCurrentBuffered->ToBase( 24 static_cast<int64_t>(nominalSourceRate) * nominalTargetRate); 25 // Buffered input frames to feed the output are removed first, so that the 26 // number of input frames required can be calculated. aCurrentBuffered may 27 // temporarily become negative. 28 *aCurrentBuffered -= aOutputDuration.ToBase(*aCurrentBuffered) * 29 correctedRate / nominalSourceRate; 30 // Determine the input duration (aligned to input frames) that would provide 31 // the specified buffering level when rounded down to the nearest input 32 // frame. 33 int64_t currentBufferedInputFrames = 34 aCurrentBuffered->ToBase<TimeUnit::FloorPolicy>(nominalSourceRate) 35 .ToTicksAtRate(nominalSourceRate); 36 TimeUnit inputDuration( 37 CheckedInt64(aNextBufferedInputFrames) - currentBufferedInputFrames, 38 nominalSourceRate); 39 EXPECT_GE(inputDuration.ToTicksAtRate(nominalSourceRate), 0); 40 *aCurrentBuffered += inputDuration; 41 // The buffer size is not used in the controller logic. 42 uint32_t bufferSize = 0; 43 aController->UpdateClock(inputDuration, aOutputDuration, 44 aNextBufferedInputFrames, bufferSize); 45 } 46 47 TEST(TestDriftController, Basic) 48 { 49 constexpr uint32_t buffered = 5 * 480; 50 constexpr uint32_t bufferedLow = 3 * 480; 51 constexpr uint32_t bufferedHigh = 7 * 480; 52 53 TimeUnit currentBuffered(buffered, 48000); 54 DriftController c(48000, 48000, currentBuffered); 55 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000U); 56 57 // The adjustment interval is 1s. 58 const auto oneSec = media::TimeUnit(48000, 48000); 59 uint32_t stepsPerSec = 50; 60 media::TimeUnit stepDuration = oneSec / stepsPerSec; 61 62 for (uint32_t i = 0; i < stepsPerSec; ++i) { 63 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 64 } 65 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 66 67 for (uint32_t i = 0; i < stepsPerSec; ++i) { 68 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedLow); 69 } 70 EXPECT_EQ(c.GetCorrectedSourceRate(), 47957u); 71 72 for (uint32_t i = 0; i < stepsPerSec; ++i) { 73 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 74 } 75 EXPECT_EQ(c.GetCorrectedSourceRate(), 47957u); 76 77 for (uint32_t i = 0; i < stepsPerSec; ++i) { 78 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 79 } 80 EXPECT_EQ(c.GetCorrectedSourceRate(), 48005u); 81 } 82 83 TEST(TestDriftController, BasicResampler) 84 { 85 // This test is equivalent to Basic, but for the output sample rate, so 86 // input buffer frame counts should be equal to those in Basic. 87 constexpr uint32_t buffered = 5 * 480; 88 constexpr uint32_t bufferedLow = 3 * 480; 89 constexpr uint32_t bufferedHigh = 7 * 480; 90 91 TimeUnit currentBuffered(buffered, 48000); 92 DriftController c(48000, 24000, currentBuffered); 93 94 // The adjustment interval is 1s. 95 const auto oneSec = media::TimeUnit(48000, 48000); 96 uint32_t stepsPerSec = 50; 97 media::TimeUnit stepDuration = oneSec / stepsPerSec; 98 99 for (uint32_t i = 0; i < stepsPerSec; ++i) { 100 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 101 } 102 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 103 104 // low 105 for (uint32_t i = 0; i < stepsPerSec; ++i) { 106 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedLow); 107 } 108 EXPECT_EQ(c.GetCorrectedSourceRate(), 47957u); 109 110 // high 111 for (uint32_t i = 0; i < stepsPerSec; ++i) { 112 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 113 } 114 EXPECT_EQ(c.GetCorrectedSourceRate(), 47957u); 115 116 // high 117 for (uint32_t i = 0; i < stepsPerSec; ++i) { 118 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 119 } 120 EXPECT_EQ(c.GetCorrectedSourceRate(), 48005u); 121 } 122 123 TEST(TestDriftController, BufferedInput) 124 { 125 constexpr uint32_t buffered = 5 * 480; 126 constexpr uint32_t bufferedLow = 3 * 480; 127 constexpr uint32_t bufferedHigh = 7 * 480; 128 129 TimeUnit currentBuffered(buffered, 48000); 130 DriftController c(48000, 48000, currentBuffered); 131 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 132 133 // The adjustment interval is 1s. 134 const auto oneSec = media::TimeUnit(48000, 48000); 135 uint32_t stepsPerSec = 20; 136 media::TimeUnit stepDuration = oneSec / stepsPerSec; 137 138 for (uint32_t i = 0; i < stepsPerSec; ++i) { 139 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 140 } 141 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 142 143 // 0 buffered when updating correction 144 for (uint32_t i = 0; i < stepsPerSec; ++i) { 145 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, 0); 146 } 147 EXPECT_EQ(c.GetCorrectedSourceRate(), 47990u); 148 149 for (uint32_t i = 0; i < stepsPerSec; ++i) { 150 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedLow); 151 } 152 EXPECT_EQ(c.GetCorrectedSourceRate(), 47971u); 153 154 for (uint32_t i = 0; i < stepsPerSec; ++i) { 155 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 156 } 157 EXPECT_EQ(c.GetCorrectedSourceRate(), 47960u); 158 159 for (uint32_t i = 0; i < stepsPerSec; ++i) { 160 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 161 } 162 // Hysteresis keeps the corrected rate the same. 163 EXPECT_EQ(c.GetCorrectedSourceRate(), 47960u); 164 } 165 166 TEST(TestDriftController, BufferedInputWithResampling) 167 { 168 // This test is equivalent to BufferedInput, but for the output sample rate, 169 // so input buffer frame counts should be equal to those in BufferedInput. 170 constexpr uint32_t buffered = 5 * 480; 171 constexpr uint32_t bufferedLow = 3 * 480; 172 constexpr uint32_t bufferedHigh = 7 * 480; 173 174 TimeUnit currentBuffered(buffered, 48000); 175 DriftController c(48000, 24000, currentBuffered); 176 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 177 178 // The adjustment interval is 1s. 179 const auto oneSec = media::TimeUnit(24000, 24000); 180 uint32_t stepsPerSec = 20; 181 media::TimeUnit stepDuration = oneSec / stepsPerSec; 182 183 for (uint32_t i = 0; i < stepsPerSec; ++i) { 184 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 185 } 186 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 187 188 // 0 buffered when updating correction 189 for (uint32_t i = 0; i < stepsPerSec; ++i) { 190 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, 0); 191 } 192 EXPECT_EQ(c.GetCorrectedSourceRate(), 47990u); 193 194 for (uint32_t i = 0; i < stepsPerSec; ++i) { 195 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedLow); 196 } 197 EXPECT_EQ(c.GetCorrectedSourceRate(), 47971u); 198 199 for (uint32_t i = 0; i < stepsPerSec; ++i) { 200 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 201 } 202 EXPECT_EQ(c.GetCorrectedSourceRate(), 47960u); 203 204 for (uint32_t i = 0; i < stepsPerSec; ++i) { 205 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 206 } 207 // Hysteresis keeps the corrected rate the same. 208 EXPECT_EQ(c.GetCorrectedSourceRate(), 47960u); 209 } 210 211 TEST(TestDriftController, SmallError) 212 { 213 constexpr uint32_t buffered = 5 * 480; 214 constexpr uint32_t bufferedLow = buffered - 48; 215 constexpr uint32_t bufferedHigh = buffered + 48; 216 217 TimeUnit currentBuffered(buffered, 48000); 218 DriftController c(48000, 48000, currentBuffered); 219 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 220 221 // The adjustment interval is 1s. 222 const auto oneSec = media::TimeUnit(48000, 48000); 223 uint32_t stepsPerSec = 25; 224 media::TimeUnit stepDuration = oneSec / stepsPerSec; 225 226 for (uint32_t i = 0; i < stepsPerSec; ++i) { 227 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, buffered); 228 } 229 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 230 231 for (uint32_t i = 0; i < stepsPerSec; ++i) { 232 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedLow); 233 } 234 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 235 236 for (uint32_t i = 0; i < stepsPerSec; ++i) { 237 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 238 } 239 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 240 for (uint32_t i = 0; i < stepsPerSec; ++i) { 241 AdvanceByOutputDuration(¤tBuffered, &c, stepDuration, bufferedHigh); 242 } 243 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000u); 244 } 245 246 TEST(TestDriftController, SmallBufferedFrames) 247 { 248 constexpr uint32_t bufferedLow = 3 * 480; 249 250 DriftController c(48000, 48000, media::TimeUnit::FromSeconds(0.05)); 251 media::TimeUnit oneSec = media::TimeUnit::FromSeconds(1); 252 uint32_t stepsPerSec = 40; 253 media::TimeUnit stepDuration = oneSec / stepsPerSec; 254 255 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000U); 256 for (uint32_t i = 0; i < stepsPerSec - 1; ++i) { 257 c.UpdateClock(stepDuration, stepDuration, bufferedLow, 0); 258 } 259 EXPECT_EQ(c.GetCorrectedSourceRate(), 48000U); 260 c.UpdateClock(stepDuration, stepDuration, bufferedLow, 0); 261 EXPECT_EQ(c.GetCorrectedSourceRate(), 47996U); 262 } 263 264 TEST(TestDriftController, VerySmallBufferedFrames) 265 { 266 uint32_t bufferedLow = 1; 267 uint32_t nominalRate = 48000; 268 269 DriftController c(nominalRate, nominalRate, media::TimeUnit::FromSeconds(1)); 270 EXPECT_EQ(c.GetCorrectedSourceRate(), nominalRate); 271 272 TimeUnit currentBuffered(bufferedLow, 48000); 273 media::TimeUnit hundredMillis = media::TimeUnit(100, 1000); 274 uint32_t previousCorrected = nominalRate; 275 // Perform enough steps (1500 seconds) that the corrected rate can 276 // get to its lower bound, without underflowing zero. 277 for (uint32_t i = 0; i < 15000; ++i) { 278 // The input packet size is reduced each iteration by as much as possible 279 // without completely draining the buffer. 280 AdvanceByOutputDuration(¤tBuffered, &c, hundredMillis, bufferedLow); 281 uint32_t correctedRate = c.GetCorrectedSourceRate(); 282 EXPECT_LE(correctedRate, previousCorrected) << "for i=" << i; 283 EXPECT_GT(correctedRate, 0u) << "for i=" << i; 284 previousCorrected = correctedRate; 285 } 286 // Check that the corrected rate has reached, does not go beyond, and does 287 // not bounce off its lower bound. 288 EXPECT_EQ(previousCorrected, 1u); 289 for (uint32_t i = 15000; i < 15010; ++i) { 290 AdvanceByOutputDuration(¤tBuffered, &c, hundredMillis, bufferedLow); 291 EXPECT_EQ(c.GetCorrectedSourceRate(), 1u) << "for i=" << i; 292 } 293 } 294 295 TEST(TestDriftController, SmallStepResponse) 296 { 297 // The DriftController is configured with nominal source rate a little less 298 // than the actual rate. 299 uint32_t nominalTargetRate = 48000; 300 uint32_t nominalSourceRate = 48000; 301 uint32_t actualSourceRate = 48000 * 1001 / 1000; // +0.1% drift 302 303 TimeUnit desiredBuffered = TimeUnit::FromSeconds(0.05); // 50 ms 304 DriftController c(nominalSourceRate, nominalTargetRate, desiredBuffered); 305 EXPECT_EQ(c.GetCorrectedSourceRate(), nominalSourceRate); 306 307 uint32_t stepsPerSec = 25; 308 // Initial buffer level == desired. Choose a base to exactly track 309 // fractions of frames buffered in the resampler. 310 TimeUnit buffered = desiredBuffered.ToBase(nominalSourceRate * stepsPerSec); 311 media::TimeUnit inputStepDuration(actualSourceRate, 312 stepsPerSec * nominalSourceRate); 313 media::TimeUnit outputStepDuration(nominalTargetRate, 314 stepsPerSec * nominalTargetRate); 315 316 // Perform enough steps to observe convergence. 317 uint32_t iterationCount = 200 /*seconds*/ * stepsPerSec; 318 for (uint32_t i = 0; i < iterationCount; ++i) { 319 uint32_t correctedRate = c.GetCorrectedSourceRate(); 320 buffered += TimeUnit(CheckedInt64(actualSourceRate) - correctedRate, 321 stepsPerSec * nominalSourceRate); 322 // The buffer size is not used in the controller logic. 323 c.UpdateClock(inputStepDuration, outputStepDuration, 324 buffered.ToTicksAtRate(nominalSourceRate), 0); 325 if (outputStepDuration * i > TimeUnit::FromSeconds(50) && 326 /* Corrections are performed only once per second. */ 327 i % stepsPerSec == 0) { 328 EXPECT_EQ(c.GetCorrectedSourceRate(), actualSourceRate) << "for i=" << i; 329 EXPECT_NEAR(buffered.ToTicksAtRate(nominalSourceRate), 330 desiredBuffered.ToTicksAtRate(nominalSourceRate), 10) 331 << "for i=" << i; 332 } 333 } 334 } 335 336 TEST(TestDriftController, LargeStepResponse) 337 { 338 // The DriftController is configured with nominal source rate much less than 339 // the actual rate. The large difference between nominal and actual 340 // produces large PID terms and capping of the change in resampler input 341 // rate to nominalRate/1000. This does not correspond exactly to an 342 // expected use case, but tests the stability of the response when changes 343 // are capped. 344 uint32_t nominalTargetRate = 48000; 345 uint32_t nominalSourceRate = 48000 * 7 / 8; 346 uint32_t actualSourceRate = 48000; 347 348 TimeUnit desiredBuffered(actualSourceRate * 10, nominalSourceRate); 349 DriftController c(nominalSourceRate, nominalTargetRate, desiredBuffered); 350 EXPECT_EQ(c.GetCorrectedSourceRate(), nominalSourceRate); 351 352 uint32_t stepsPerSec = 20; 353 // Initial buffer level == desired. Choose a base to exactly track 354 // fractions of frames buffered in the resampler. 355 TimeUnit buffered = desiredBuffered.ToBase(nominalSourceRate * stepsPerSec); 356 media::TimeUnit inputStepDuration(actualSourceRate, 357 stepsPerSec * nominalSourceRate); 358 media::TimeUnit outputStepDuration(nominalTargetRate, 359 stepsPerSec * nominalTargetRate); 360 361 // Changes in the corrected rate are limited to nominalRate/1000 per second. 362 // Perform enough steps to get from nominal to actual source rate and then 363 // observe convergence. 364 uint32_t iterationCount = 8 * stepsPerSec * 1000 * 365 (actualSourceRate - nominalSourceRate) / 366 nominalSourceRate; 367 EXPECT_GT(outputStepDuration * (iterationCount - 1), 368 TimeUnit::FromSeconds(1020)); 369 for (uint32_t i = 0; i < iterationCount; ++i) { 370 uint32_t correctedRate = c.GetCorrectedSourceRate(); 371 buffered += TimeUnit(CheckedInt64(actualSourceRate) - correctedRate, 372 stepsPerSec * nominalSourceRate); 373 // The buffer size is not used in the controller logic. 374 c.UpdateClock(inputStepDuration, outputStepDuration, 375 buffered.ToTicksAtRate(nominalSourceRate), 0); 376 if (outputStepDuration * i > TimeUnit::FromSeconds(1020) && 377 /* Corrections are performed only once per second. */ 378 i % stepsPerSec == 0) { 379 EXPECT_EQ(c.GetCorrectedSourceRate(), actualSourceRate) << "for i=" << i; 380 EXPECT_NEAR(buffered.ToTicksAtRate(nominalSourceRate), 381 desiredBuffered.ToTicksAtRate(nominalSourceRate), 10) 382 << "for i=" << i; 383 } 384 } 385 }