• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 "VideoFrameScheduler2"
19 #include <utils/Log.h>
20 #define ATRACE_TAG ATRACE_TAG_VIDEO
21 #include <utils/Mutex.h>
22 #include <utils/Thread.h>
23 #include <utils/Trace.h>
24 
25 #include <algorithm>
26 #include <jni.h>
27 #include <math.h>
28 
29 #include <android/choreographer.h>
30 #include <android/looper.h>
31 #include <media/stagefright/VideoFrameScheduler2.h>
32 #include <mediaplayer2/JavaVMHelper.h>
33 
34 #include <media/stagefright/foundation/ADebug.h>
35 #include <media/stagefright/foundation/AUtils.h>
36 
37 namespace android {
38 
39 static void getVsyncOffset(nsecs_t* appVsyncOffsetPtr, nsecs_t* sfVsyncOffsetPtr);
40 
41 /* ======================================================================= */
42 /*                               VsyncTracker                              */
43 /* ======================================================================= */
44 
45 class VsyncTracker : public RefBase{
46 public:
47     VsyncTracker();
~VsyncTracker()48     ~VsyncTracker() {}
49     nsecs_t getVsyncPeriod();
50     nsecs_t getVsyncTime(nsecs_t periodOffset);
51     void addSample(nsecs_t timestamp);
52 
53 private:
54     static const int kMaxSamples = 32;
55     static const int kMinSamplesForUpdate = 6;
56     int mNumSamples;
57     int mFirstSample;
58     nsecs_t mReferenceTime;
59     nsecs_t mPhase;
60     nsecs_t mPeriod;
61     nsecs_t mTimestampSamples[kMaxSamples];
62     Mutex mLock;
63 
64     void updateModelLocked();
65 };
66 
VsyncTracker()67 VsyncTracker::VsyncTracker()
68     : mNumSamples(0),
69       mFirstSample(0),
70       mReferenceTime(0),
71       mPhase(0),
72       mPeriod(0) {
73     for (int i = 0; i < kMaxSamples; i++) {
74         mTimestampSamples[i] = 0;
75     }
76 }
77 
getVsyncPeriod()78 nsecs_t VsyncTracker::getVsyncPeriod() {
79     Mutex::Autolock dataLock(mLock);
80     return mPeriod;
81 }
82 
getVsyncTime(nsecs_t periodOffset)83 nsecs_t VsyncTracker::getVsyncTime(nsecs_t periodOffset) {
84     Mutex::Autolock dataLock(mLock);
85     const nsecs_t now = systemTime();
86     nsecs_t phase = mReferenceTime + mPhase;
87 
88     // result = (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase
89     // prevent overflow
90     nsecs_t result = (now - phase) / mPeriod;
91     if (result > LONG_LONG_MAX - periodOffset - 1) {
92         return LONG_LONG_MAX;
93     } else {
94         result += periodOffset + 1;
95     }
96     if (result > LONG_LONG_MAX / mPeriod) {
97         return LONG_LONG_MAX;
98     } else {
99         result *= mPeriod;
100     }
101     if (result > LONG_LONG_MAX - phase) {
102         return LONG_LONG_MAX;
103     } else {
104         result += phase;
105     }
106 
107     return result;
108 }
109 
addSample(nsecs_t timestamp)110 void VsyncTracker::addSample(nsecs_t timestamp) {
111     Mutex::Autolock dataLock(mLock);
112     if (mNumSamples == 0) {
113         mPhase = 0;
114         mReferenceTime = timestamp;
115     }
116     int idx = (mFirstSample + mNumSamples) % kMaxSamples;
117     mTimestampSamples[idx] = timestamp;
118     if (mNumSamples < kMaxSamples) {
119         mNumSamples++;
120     } else {
121         mFirstSample = (mFirstSample + 1) % kMaxSamples;
122     }
123     updateModelLocked();
124 }
125 
updateModelLocked()126 void VsyncTracker::updateModelLocked() {
127     if (mNumSamples < kMinSamplesForUpdate) {
128         return;
129     }
130     nsecs_t durationSum = 0;
131     nsecs_t minDuration = LONG_MAX;
132     nsecs_t maxDuration = 0;
133 
134     for (int i = 1; i < mNumSamples; i++) {
135         int idx = (mFirstSample + i) % kMaxSamples;
136         int prev = (idx + kMaxSamples - 1) % kMaxSamples;
137         long duration = mTimestampSamples[idx] - mTimestampSamples[prev];
138         durationSum += duration;
139         if (minDuration > duration) { minDuration = duration; }
140         if (maxDuration < duration) { maxDuration = duration; }
141     }
142 
143     durationSum -= (minDuration + maxDuration);
144     mPeriod = durationSum / (mNumSamples - 3);
145 
146     double sampleAvgX = 0.0;
147     double sampleAvgY = 0.0;
148     double scale = 2.0 * M_PI / (double) mPeriod;
149 
150     for (int i = 1; i < mNumSamples; i++) {
151         int idx = (mFirstSample + i) % kMaxSamples;
152         long sample = mTimestampSamples[idx] - mReferenceTime;
153         double samplePhase = (double) (sample % mPeriod) * scale;
154         sampleAvgX += cos(samplePhase);
155         sampleAvgY += sin(samplePhase);
156     }
157 
158     sampleAvgX /= (double) mNumSamples - 1.0;
159     sampleAvgY /= (double) mNumSamples - 1.0;
160     mPhase = (long) (atan2(sampleAvgY, sampleAvgX) / scale);
161 }
162 
frameCallback(int64_t frameTimeNanos,void * data)163 static void frameCallback(int64_t frameTimeNanos, void* data) {
164     if (data == NULL) {
165         return;
166     }
167     sp<VsyncTracker> vsyncTracker(static_cast<VsyncTracker*>(data));
168     vsyncTracker->addSample(frameTimeNanos);
169     AChoreographer_postFrameCallback64(AChoreographer_getInstance(),
170             frameCallback, static_cast<void*>(vsyncTracker.get()));
171 }
172 
173 /* ======================================================================= */
174 /*                                   JNI                                   */
175 /* ======================================================================= */
176 
getVsyncOffset(nsecs_t * appVsyncOffsetPtr,nsecs_t * sfVsyncOffsetPtr)177 static void getVsyncOffset(nsecs_t* appVsyncOffsetPtr, nsecs_t* sfVsyncOffsetPtr) {
178     static const nsecs_t kOneMillisecInNanosec = 1000000;
179     static const nsecs_t kOneSecInNanosec = kOneMillisecInNanosec * 1000;
180 
181     JNIEnv *env = JavaVMHelper::getJNIEnv();
182     jclass jDisplayManagerGlobalCls = env->FindClass(
183             "android/hardware/display/DisplayManagerGlobal");
184     jclass jDisplayCls = env->FindClass("android/view/Display");
185 
186     jmethodID jGetInstance = env->GetStaticMethodID(jDisplayManagerGlobalCls,
187             "getInstance", "()Landroid/hardware/display/DisplayManagerGlobal;");
188     jobject javaDisplayManagerGlobalObj = env->CallStaticObjectMethod(
189             jDisplayManagerGlobalCls, jGetInstance);
190 
191     jfieldID jDEFAULT_DISPLAY = env->GetStaticFieldID(jDisplayCls, "DEFAULT_DISPLAY", "I");
192     jint DEFAULT_DISPLAY = env->GetStaticIntField(jDisplayCls, jDEFAULT_DISPLAY);
193 
194     jmethodID jgetRealDisplay = env->GetMethodID(jDisplayManagerGlobalCls,
195             "getRealDisplay", "(I)Landroid/view/Display;");
196     jobject javaDisplayObj = env->CallObjectMethod(
197             javaDisplayManagerGlobalObj, jgetRealDisplay, DEFAULT_DISPLAY);
198 
199     jmethodID jGetRefreshRate = env->GetMethodID(jDisplayCls, "getRefreshRate", "()F");
200     jfloat javaRefreshRate = env->CallFloatMethod(javaDisplayObj, jGetRefreshRate);
201     nsecs_t vsyncPeriod = (nsecs_t) (kOneSecInNanosec / (float) javaRefreshRate);
202 
203     jmethodID jGetAppVsyncOffsetNanos = env->GetMethodID(
204             jDisplayCls, "getAppVsyncOffsetNanos", "()J");
205     jlong javaAppVsyncOffset = env->CallLongMethod(javaDisplayObj, jGetAppVsyncOffsetNanos);
206     *appVsyncOffsetPtr = (nsecs_t) javaAppVsyncOffset;
207 
208     jmethodID jGetPresentationDeadlineNanos = env->GetMethodID(
209             jDisplayCls, "getPresentationDeadlineNanos", "()J");
210     jlong javaPresentationDeadline = env->CallLongMethod(
211             javaDisplayObj, jGetPresentationDeadlineNanos);
212 
213     *sfVsyncOffsetPtr = vsyncPeriod - ((nsecs_t) javaPresentationDeadline - kOneMillisecInNanosec);
214 }
215 
216 /* ======================================================================= */
217 /*                          Choreographer Thread                           */
218 /* ======================================================================= */
219 
220 struct ChoreographerThread : public Thread {
221     ChoreographerThread(bool canCallJava);
222     status_t init(void* data);
223     virtual status_t readyToRun() override;
224     virtual bool threadLoop() override;
225 
226 protected:
~ChoreographerThreadandroid::ChoreographerThread227     virtual ~ChoreographerThread() {}
228 
229 private:
230     DISALLOW_EVIL_CONSTRUCTORS(ChoreographerThread);
231     void* mData;
232 };
233 
ChoreographerThread(bool canCallJava)234 ChoreographerThread::ChoreographerThread(bool canCallJava) : Thread(canCallJava) {
235 }
236 
init(void * data)237 status_t ChoreographerThread::init(void* data) {
238     if (data == NULL) {
239         return NO_INIT;
240     }
241     mData = data;
242     return OK;
243 }
244 
readyToRun()245 status_t ChoreographerThread::readyToRun() {
246     ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
247     if (AChoreographer_getInstance() == NULL) {
248         return NO_INIT;
249     }
250     AChoreographer_postFrameCallback64(AChoreographer_getInstance(), frameCallback, mData);
251     return OK;
252 }
253 
threadLoop()254 bool ChoreographerThread::threadLoop() {
255     ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
256     return true;
257 }
258 
259 /* ======================================================================= */
260 /*                             Frame Scheduler                             */
261 /* ======================================================================= */
262 
VideoFrameScheduler2()263 VideoFrameScheduler2::VideoFrameScheduler2() : VideoFrameSchedulerBase() {
264 
265     getVsyncOffset(&mAppVsyncOffset, &mSfVsyncOffset);
266 
267     Mutex::Autolock threadLock(mLock);
268     mChoreographerThread = new ChoreographerThread(true);
269 
270     mVsyncTracker = new VsyncTracker();
271     if (mChoreographerThread->init(static_cast<void*>(mVsyncTracker.get())) != OK) {
272         mChoreographerThread.clear();
273     }
274     if (mChoreographerThread != NULL && mChoreographerThread->run("Choreographer") != OK) {
275         mChoreographerThread.clear();
276     }
277 }
278 
updateVsync()279 void VideoFrameScheduler2::updateVsync() {
280     mVsyncTime = 0;
281     mVsyncPeriod = 0;
282 
283     if (mVsyncTracker != NULL) {
284         mVsyncPeriod = mVsyncTracker->getVsyncPeriod();
285         mVsyncTime = mVsyncTracker->getVsyncTime(mSfVsyncOffset - mAppVsyncOffset);
286     }
287     mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod;
288 }
289 
release()290 void VideoFrameScheduler2::release() {
291     // Do not change order
292     {
293         Mutex::Autolock threadLock(mLock);
294         mChoreographerThread->requestExitAndWait();
295         mChoreographerThread.clear();
296     }
297 
298     mVsyncTracker.clear();
299 }
300 
~VideoFrameScheduler2()301 VideoFrameScheduler2::~VideoFrameScheduler2() {
302     release();
303 }
304 
305 } // namespace android
306