ClockEstimator.cpp revision aa8b569eb652c22821b93a6e543449a52ad21158
1/* 2** 3** Copyright 2014, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18//#define LOG_NDEBUG 0 19#define LOG_TAG "ClockEstimator" 20#include <utils/Log.h> 21 22#include <math.h> 23#include <media/stagefright/ClockEstimator.h> 24 25#include <media/stagefright/foundation/ADebug.h> 26 27namespace android { 28 29WindowedLinearFitEstimator::WindowedLinearFitEstimator( 30 size_t headLength, double headFactor, size_t mainLength, double tailFactor) 31 : mHeadFactorInv(1. / headFactor), 32 mTailFactor(tailFactor), 33 mHistoryLength(mainLength + headLength), 34 mHeadLength(headLength) { 35 reset(); 36 mXHistory.resize(mHistoryLength); 37 mYHistory.resize(mHistoryLength); 38 mFirstWeight = pow(headFactor, mHeadLength); 39} 40 41WindowedLinearFitEstimator::LinearFit::LinearFit() { 42 reset(); 43} 44 45void WindowedLinearFitEstimator::LinearFit::reset() { 46 mX = mXX = mY = mYY = mXY = mW = 0.; 47} 48 49double WindowedLinearFitEstimator::LinearFit::size() const { 50 double s = mW * mW + mX * mX + mY * mY + mXX * mXX + mXY * mXY + mYY * mYY; 51 if (s > 1e72) { 52 // 1e72 corresponds to clock monotonic time of about 8 years 53 ALOGW("estimator is overflowing: w=%g x=%g y=%g xx=%g xy=%g yy=%g", 54 mW, mX, mY, mXX, mXY, mYY); 55 } 56 return s; 57} 58 59void WindowedLinearFitEstimator::LinearFit::add(double x, double y, double w) { 60 mW += w; 61 mX += w * x; 62 mY += w * y; 63 mXX += w * x * x; 64 mXY += w * x * y; 65 mYY += w * y * y; 66} 67 68void WindowedLinearFitEstimator::LinearFit::combine(const LinearFit &lf) { 69 mW += lf.mW; 70 mX += lf.mX; 71 mY += lf.mY; 72 mXX += lf.mXX; 73 mXY += lf.mXY; 74 mYY += lf.mYY; 75} 76 77void WindowedLinearFitEstimator::LinearFit::scale(double w) { 78 mW *= w; 79 mX *= w; 80 mY *= w; 81 mXX *= w; 82 mXY *= w; 83 mYY *= w; 84} 85 86double WindowedLinearFitEstimator::LinearFit::interpolate(double x) { 87 double div = mW * mXX - mX * mX; 88 if (fabs(div) < 1e-5 * mW * mW) { 89 // this only should happen on the first value 90 return x; 91 // assuming a = 1, we could also return x + (mY - mX) / mW; 92 } 93 double a_div = (mW * mXY - mX * mY); 94 double b_div = (mXX * mY - mX * mXY); 95 ALOGV("a=%.4g b=%.4g in=%g out=%g", 96 a_div / div, b_div / div, x, (a_div * x + b_div) / div); 97 return (a_div * x + b_div) / div; 98} 99 100double WindowedLinearFitEstimator::estimate(double x, double y) { 101 /* 102 * TODO: We could update the head by adding the new sample to it 103 * and amplifying it, but this approach can lead to unbounded 104 * error. Instead, we recalculate the head at each step, which 105 * is computationally more expensive. We could balance the two 106 * methods by recalculating just before the error becomes 107 * significant. 108 */ 109 const bool update_head = false; 110 if (update_head) { 111 // add new sample to the head 112 mHead.scale(mHeadFactorInv); // amplify head 113 mHead.add(x, y, mFirstWeight); 114 } 115 116 /* 117 * TRICKY: place elements into the circular buffer at decreasing 118 * indices, so that we can access past elements by addition 119 * (thereby avoiding potentially negative indices.) 120 */ 121 if (mNumSamples >= mHeadLength) { 122 // move last head sample from head to the main window 123 size_t lastHeadIx = (mSampleIx + mHeadLength) % mHistoryLength; 124 if (update_head) { 125 mHead.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], -1.); // remove 126 } 127 mMain.add(mXHistory[lastHeadIx], mYHistory[lastHeadIx], 1.); 128 if (mNumSamples >= mHistoryLength) { 129 // move last main sample from main window to tail 130 mMain.add(mXHistory[mSampleIx], mYHistory[mSampleIx], -1.); // remove 131 mTail.add(mXHistory[mSampleIx], mYHistory[mSampleIx], 1.); 132 mTail.scale(mTailFactor); // attenuate tail 133 } 134 } 135 136 mXHistory.editItemAt(mSampleIx) = x; 137 mYHistory.editItemAt(mSampleIx) = y; 138 if (mNumSamples < mHistoryLength) { 139 ++mNumSamples; 140 } 141 142 // recalculate head unless we were using the update method 143 if (!update_head) { 144 mHead.reset(); 145 double w = mFirstWeight; 146 for (size_t headIx = 0; headIx < mHeadLength && headIx < mNumSamples; ++headIx) { 147 size_t ix = (mSampleIx + headIx) % mHistoryLength; 148 mHead.add(mXHistory[ix], mYHistory[ix], w); 149 w *= mHeadFactorInv; 150 } 151 } 152 153 if (mSampleIx > 0) { 154 --mSampleIx; 155 } else { 156 mSampleIx = mHistoryLength - 1; 157 } 158 159 // return estimation result 160 LinearFit total; 161 total.combine(mHead); 162 total.combine(mMain); 163 total.combine(mTail); 164 return total.interpolate(x); 165} 166 167void WindowedLinearFitEstimator::reset() { 168 mHead.reset(); 169 mMain.reset(); 170 mTail.reset(); 171 mNumSamples = 0; 172 mSampleIx = mHistoryLength - 1; 173} 174 175}; // namespace android 176 177 178