1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "VideoFrameScheduler" 19 #include <utils/Log.h> 20 #define ATRACE_TAG ATRACE_TAG_VIDEO 21 #include <utils/Trace.h> 22 23 #include <sys/time.h> 24 25 #include <binder/IServiceManager.h> 26 #include <gui/ISurfaceComposer.h> 27 #include <ui/DisplayStatInfo.h> 28 29 #include <media/stagefright/foundation/ADebug.h> 30 #include <media/stagefright/foundation/AUtils.h> 31 32 #include "VideoFrameScheduler.h" 33 34 namespace android { 35 36 static const nsecs_t kNanosIn1s = 1000000000; 37 38 template<class T> 39 static int compare(const T *lhs, const T *rhs) { 40 if (*lhs < *rhs) { 41 return -1; 42 } else if (*lhs > *rhs) { 43 return 1; 44 } else { 45 return 0; 46 } 47 } 48 49 /* ======================================================================= */ 50 /* PLL */ 51 /* ======================================================================= */ 52 53 static const size_t kMinSamplesToStartPrime = 3; 54 static const size_t kMinSamplesToStopPrime = VideoFrameScheduler::kHistorySize; 55 static const size_t kMinSamplesToEstimatePeriod = 3; 56 static const size_t kMaxSamplesToEstimatePeriod = VideoFrameScheduler::kHistorySize; 57 58 static const size_t kPrecision = 12; 59 static const size_t kErrorThreshold = (1 << (kPrecision * 2)) / 10; 60 static const int64_t kMultiplesThresholdDiv = 4; // 25% 61 static const int64_t kReFitThresholdDiv = 100; // 1% 62 static const nsecs_t kMaxAllowedFrameSkip = kNanosIn1s; // 1 sec 63 static const nsecs_t kMinPeriod = kNanosIn1s / 120; // 120Hz 64 static const nsecs_t kRefitRefreshPeriod = 10 * kNanosIn1s; // 10 sec 65 66 VideoFrameScheduler::PLL::PLL() 67 : mPeriod(-1), 68 mPhase(0), 69 mPrimed(false), 70 mSamplesUsedForPriming(0), 71 mLastTime(-1), 72 mNumSamples(0) { 73 } 74 75 void VideoFrameScheduler::PLL::reset(float fps) { 76 //test(); 77 78 mSamplesUsedForPriming = 0; 79 mLastTime = -1; 80 81 // set up or reset video PLL 82 if (fps <= 0.f) { 83 mPeriod = -1; 84 mPrimed = false; 85 } else { 86 ALOGV("reset at %.1f fps", fps); 87 mPeriod = (nsecs_t)(1e9 / fps + 0.5); 88 mPrimed = true; 89 } 90 91 restart(); 92 } 93 94 // reset PLL but keep previous period estimate 95 void VideoFrameScheduler::PLL::restart() { 96 mNumSamples = 0; 97 mPhase = -1; 98 } 99 100 #if 0 101 102 void VideoFrameScheduler::PLL::test() { 103 nsecs_t period = kNanosIn1s / 60; 104 mTimes[0] = 0; 105 mTimes[1] = period; 106 mTimes[2] = period * 3; 107 mTimes[3] = period * 4; 108 mTimes[4] = period * 7; 109 mTimes[5] = period * 8; 110 mTimes[6] = period * 10; 111 mTimes[7] = period * 12; 112 mNumSamples = 8; 113 int64_t a, b, err; 114 fit(0, period * 12 / 7, 8, &a, &b, &err); 115 // a = 0.8(5)+ 116 // b = -0.14097(2)+ 117 // err = 0.2750578(703)+ 118 ALOGD("a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", 119 (long long)a, (a / (float)(1 << kPrecision)), 120 (long long)b, (b / (float)(1 << kPrecision)), 121 (long long)err, (err / (float)(1 << (kPrecision * 2)))); 122 } 123 124 #endif 125 126 bool VideoFrameScheduler::PLL::fit( 127 nsecs_t phase, nsecs_t period, size_t numSamplesToUse, 128 int64_t *a, int64_t *b, int64_t *err) { 129 if (numSamplesToUse > mNumSamples) { 130 numSamplesToUse = mNumSamples; 131 } 132 133 int64_t sumX = 0; 134 int64_t sumXX = 0; 135 int64_t sumXY = 0; 136 int64_t sumYY = 0; 137 int64_t sumY = 0; 138 139 int64_t x = 0; // x usually is in [0..numSamplesToUse) 140 nsecs_t lastTime; 141 for (size_t i = 0; i < numSamplesToUse; i++) { 142 size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize; 143 nsecs_t time = mTimes[ix]; 144 if (i > 0) { 145 x += divRound(time - lastTime, period); 146 } 147 // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision 148 // ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during 149 // priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod 150 // while we are not refitting. 151 int64_t y = divRound(time - phase, period >> kPrecision); 152 sumX += x; 153 sumY += y; 154 sumXX += x * x; 155 sumXY += x * y; 156 sumYY += y * y; 157 lastTime = time; 158 } 159 160 int64_t div = numSamplesToUse * sumXX - sumX * sumX; 161 if (div == 0) { 162 return false; 163 } 164 165 int64_t a_nom = numSamplesToUse * sumXY - sumX * sumY; 166 int64_t b_nom = sumXX * sumY - sumX * sumXY; 167 *a = divRound(a_nom, div); 168 *b = divRound(b_nom, div); 169 // don't use a and b directly as the rounding error is significant 170 *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div); 171 ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", 172 numSamplesToUse, 173 (long long)*a, (*a / (float)(1 << kPrecision)), 174 (long long)*b, (*b / (float)(1 << kPrecision)), 175 (long long)*err, (*err / (float)(1 << (kPrecision * 2)))); 176 return true; 177 } 178 179 void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) { 180 if (numSamplesToUse > mNumSamples) { 181 numSamplesToUse = mNumSamples; 182 } 183 CHECK(numSamplesToUse >= 3); // must have at least 3 samples 184 185 // estimate video framerate from deltas between timestamps, and 186 // 2nd order deltas 187 Vector<nsecs_t> deltas; 188 nsecs_t lastTime, firstTime; 189 for (size_t i = 0; i < numSamplesToUse; ++i) { 190 size_t index = (mNumSamples - numSamplesToUse + i) % kHistorySize; 191 nsecs_t time = mTimes[index]; 192 if (i > 0) { 193 if (time - lastTime > kMinPeriod) { 194 //ALOGV("delta: %lld", (long long)(time - lastTime)); 195 deltas.push(time - lastTime); 196 } 197 } else { 198 firstTime = time; 199 } 200 lastTime = time; 201 } 202 deltas.sort(compare<nsecs_t>); 203 size_t numDeltas = deltas.size(); 204 if (numDeltas > 1) { 205 nsecs_t deltaMinLimit = max(deltas[0] / kMultiplesThresholdDiv, kMinPeriod); 206 nsecs_t deltaMaxLimit = deltas[numDeltas / 2] * kMultiplesThresholdDiv; 207 for (size_t i = numDeltas / 2 + 1; i < numDeltas; ++i) { 208 if (deltas[i] > deltaMaxLimit) { 209 deltas.resize(i); 210 numDeltas = i; 211 break; 212 } 213 } 214 for (size_t i = 1; i < numDeltas; ++i) { 215 nsecs_t delta2nd = deltas[i] - deltas[i - 1]; 216 if (delta2nd >= deltaMinLimit) { 217 //ALOGV("delta2: %lld", (long long)(delta2nd)); 218 deltas.push(delta2nd); 219 } 220 } 221 } 222 223 // use the one that yields the best match 224 int64_t bestScore; 225 for (size_t i = 0; i < deltas.size(); ++i) { 226 nsecs_t delta = deltas[i]; 227 int64_t score = 0; 228 #if 1 229 // simplest score: number of deltas that are near multiples 230 size_t matches = 0; 231 for (size_t j = 0; j < deltas.size(); ++j) { 232 nsecs_t err = periodicError(deltas[j], delta); 233 if (err < delta / kMultiplesThresholdDiv) { 234 ++matches; 235 } 236 } 237 score = matches; 238 #if 0 239 // could be weighed by the (1 - normalized error) 240 if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { 241 int64_t a, b, err; 242 fit(firstTime, delta, numSamplesToUse, &a, &b, &err); 243 err = (1 << (2 * kPrecision)) - err; 244 score *= max(0, err); 245 } 246 #endif 247 #else 248 // or use the error as a negative score 249 if (numSamplesToUse >= kMinSamplesToEstimatePeriod) { 250 int64_t a, b, err; 251 fit(firstTime, delta, numSamplesToUse, &a, &b, &err); 252 score = -delta * err; 253 } 254 #endif 255 if (i == 0 || score > bestScore) { 256 bestScore = score; 257 mPeriod = delta; 258 mPhase = firstTime; 259 } 260 } 261 ALOGV("priming[%zu] phase:%lld period:%lld", numSamplesToUse, mPhase, mPeriod); 262 } 263 264 nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) { 265 if (mLastTime >= 0 266 // if time goes backward, or we skipped rendering 267 && (time > mLastTime + kMaxAllowedFrameSkip || time < mLastTime)) { 268 restart(); 269 } 270 271 mLastTime = time; 272 mTimes[mNumSamples % kHistorySize] = time; 273 ++mNumSamples; 274 275 bool doFit = time > mRefitAt; 276 if ((mPeriod <= 0 || !mPrimed) && mNumSamples >= kMinSamplesToStartPrime) { 277 prime(kMinSamplesToStopPrime); 278 ++mSamplesUsedForPriming; 279 doFit = true; 280 } 281 if (mPeriod > 0 && mNumSamples >= kMinSamplesToEstimatePeriod) { 282 if (mPhase < 0) { 283 // initialize phase to the current render time 284 mPhase = time; 285 doFit = true; 286 } else if (!doFit) { 287 int64_t err = periodicError(time - mPhase, mPeriod); 288 doFit = err > mPeriod / kReFitThresholdDiv; 289 } 290 291 if (doFit) { 292 int64_t a, b, err; 293 if (!fit(mPhase, mPeriod, kMaxSamplesToEstimatePeriod, &a, &b, &err)) { 294 // samples are not suitable for fitting. this means they are 295 // also not suitable for priming. 296 ALOGV("could not fit - keeping old period:%lld", (long long)mPeriod); 297 return mPeriod; 298 } 299 300 mRefitAt = time + kRefitRefreshPeriod; 301 302 mPhase += (mPeriod * b) >> kPrecision; 303 mPeriod = (mPeriod * a) >> kPrecision; 304 ALOGV("new phase:%lld period:%lld", (long long)mPhase, (long long)mPeriod); 305 306 if (err < kErrorThreshold) { 307 if (!mPrimed && mSamplesUsedForPriming >= kMinSamplesToStopPrime) { 308 mPrimed = true; 309 } 310 } else { 311 mPrimed = false; 312 mSamplesUsedForPriming = 0; 313 } 314 } 315 } 316 return mPeriod; 317 } 318 319 /* ======================================================================= */ 320 /* Frame Scheduler */ 321 /* ======================================================================= */ 322 323 static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60; // 60Hz 324 static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s; // 1 sec 325 326 VideoFrameScheduler::VideoFrameScheduler() 327 : mVsyncTime(0), 328 mVsyncPeriod(0), 329 mVsyncRefreshAt(0), 330 mLastVsyncTime(-1), 331 mTimeCorrection(0) { 332 } 333 334 void VideoFrameScheduler::updateVsync() { 335 mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod; 336 mVsyncPeriod = 0; 337 mVsyncTime = 0; 338 339 // TODO: schedule frames for the destination surface 340 // For now, surface flinger only schedules frames on the primary display 341 if (mComposer == NULL) { 342 String16 name("SurfaceFlinger"); 343 sp<IServiceManager> sm = defaultServiceManager(); 344 mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name)); 345 } 346 if (mComposer != NULL) { 347 DisplayStatInfo stats; 348 status_t res = mComposer->getDisplayStats(NULL /* display */, &stats); 349 if (res == OK) { 350 ALOGV("vsync time:%lld period:%lld", 351 (long long)stats.vsyncTime, (long long)stats.vsyncPeriod); 352 mVsyncTime = stats.vsyncTime; 353 mVsyncPeriod = stats.vsyncPeriod; 354 } else { 355 ALOGW("getDisplayStats returned %d", res); 356 } 357 } else { 358 ALOGW("could not get surface mComposer service"); 359 } 360 } 361 362 void VideoFrameScheduler::init(float videoFps) { 363 updateVsync(); 364 365 mLastVsyncTime = -1; 366 mTimeCorrection = 0; 367 368 mPll.reset(videoFps); 369 } 370 371 void VideoFrameScheduler::restart() { 372 mLastVsyncTime = -1; 373 mTimeCorrection = 0; 374 375 mPll.restart(); 376 } 377 378 nsecs_t VideoFrameScheduler::getVsyncPeriod() { 379 if (mVsyncPeriod > 0) { 380 return mVsyncPeriod; 381 } 382 return kDefaultVsyncPeriod; 383 } 384 385 nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) { 386 nsecs_t origRenderTime = renderTime; 387 388 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 389 if (now >= mVsyncRefreshAt) { 390 updateVsync(); 391 } 392 393 // without VSYNC info, there is nothing to do 394 if (mVsyncPeriod == 0) { 395 ALOGV("no vsync: render=%lld", (long long)renderTime); 396 return renderTime; 397 } 398 399 // ensure vsync time is well before (corrected) render time 400 if (mVsyncTime > renderTime - 4 * mVsyncPeriod) { 401 mVsyncTime -= 402 ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod; 403 } 404 405 // Video presentation takes place at the VSYNC _after_ renderTime. Adjust renderTime 406 // so this effectively becomes a rounding operation (to the _closest_ VSYNC.) 407 renderTime -= mVsyncPeriod / 2; 408 409 const nsecs_t videoPeriod = mPll.addSample(origRenderTime); 410 if (videoPeriod > 0) { 411 // Smooth out rendering 412 size_t N = 12; 413 nsecs_t fiveSixthDev = 414 abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod) 415 / (mVsyncPeriod / 100); 416 // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz) 417 if (fiveSixthDev < 12) { /* 12% / 6 = 2% */ 418 N = 20; 419 } 420 421 nsecs_t offset = 0; 422 nsecs_t edgeRemainder = 0; 423 for (size_t i = 1; i <= N; i++) { 424 offset += 425 (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod; 426 edgeRemainder += (videoPeriod * i) % mVsyncPeriod; 427 } 428 mTimeCorrection += mVsyncPeriod / 2 - offset / N; 429 renderTime += mTimeCorrection; 430 nsecs_t correctionLimit = mVsyncPeriod * 3 / 5; 431 edgeRemainder = abs(edgeRemainder / N - mVsyncPeriod / 2); 432 if (edgeRemainder <= mVsyncPeriod / 3) { 433 correctionLimit /= 2; 434 } 435 436 // estimate how many VSYNCs a frame will spend on the display 437 nsecs_t nextVsyncTime = 438 renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod); 439 if (mLastVsyncTime >= 0) { 440 size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod; 441 size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod); 442 bool vsyncsPerFrameAreNearlyConstant = 443 periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0; 444 445 if (mTimeCorrection > correctionLimit && 446 (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) { 447 // remove a VSYNC 448 mTimeCorrection -= mVsyncPeriod / 2; 449 renderTime -= mVsyncPeriod / 2; 450 nextVsyncTime -= mVsyncPeriod; 451 --vsyncsForLastFrame; 452 } else if (mTimeCorrection < -correctionLimit && 453 (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) { 454 // add a VSYNC 455 mTimeCorrection += mVsyncPeriod / 2; 456 renderTime += mVsyncPeriod / 2; 457 nextVsyncTime += mVsyncPeriod; 458 ++vsyncsForLastFrame; 459 } 460 ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame); 461 } 462 mLastVsyncTime = nextVsyncTime; 463 } 464 465 // align rendertime to the center between VSYNC edges 466 renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod; 467 renderTime += mVsyncPeriod / 2; 468 ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime); 469 ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000); 470 return renderTime; 471 } 472 473 void VideoFrameScheduler::release() { 474 mComposer.clear(); 475 } 476 477 VideoFrameScheduler::~VideoFrameScheduler() { 478 release(); 479 } 480 481 } // namespace android 482 483