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