1 /*
2  * Copyright (C) 2010 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_TAG "SurfaceTexture"
18 
19 #include <stdio.h>
20 
21 #include <EGL/egl.h>
22 #include <EGL/eglext.h>
23 #include <GLES2/gl2.h>
24 #include <GLES2/gl2ext.h>
25 
26 #include <gui/GLConsumer.h>
27 #include <gui/Surface.h>
28 #include <gui/BufferQueue.h>
29 
30 #include "core_jni_helpers.h"
31 
32 #include <cutils/atomic.h>
33 #include <utils/Log.h>
34 #include <utils/misc.h>
35 
36 #include "jni.h"
37 #include <nativehelper/JNIHelp.h>
38 #include <nativehelper/ScopedLocalRef.h>
39 #include "surfacetexture/SurfaceTexture.h"
40 
41 // ----------------------------------------------------------------------------
42 
43 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
44 
45 namespace android {
46 
47 static const char* const OutOfResourcesException =
48     "android/view/Surface$OutOfResourcesException";
49 static const char* const IllegalStateException = "java/lang/IllegalStateException";
50 const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
51 
52 struct fields_t {
53     jfieldID  surfaceTexture;
54     jfieldID  producer;
55     jfieldID  frameAvailableListener;
56     jmethodID postEvent;
57 };
58 static fields_t fields;
59 
60 // Get an ID that's unique within this process.
createProcessUniqueId()61 static int32_t createProcessUniqueId() {
62     static volatile int32_t globalCounter = 0;
63     return android_atomic_inc(&globalCounter);
64 }
65 
66 // Check whether the current EGL context is protected.
isProtectedContext()67 static bool isProtectedContext() {
68     EGLDisplay dpy = eglGetCurrentDisplay();
69     EGLContext ctx = eglGetCurrentContext();
70 
71     if (dpy == EGL_NO_DISPLAY || ctx == EGL_NO_CONTEXT) {
72         return false;
73     }
74 
75     EGLint isProtected = EGL_FALSE;
76     eglQueryContext(dpy, ctx, EGL_PROTECTED_CONTENT_EXT, &isProtected);
77 
78     return isProtected;
79 }
80 
81 // ----------------------------------------------------------------------------
82 
SurfaceTexture_setSurfaceTexture(JNIEnv * env,jobject thiz,const sp<SurfaceTexture> & surfaceTexture)83 static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz,
84         const sp<SurfaceTexture>& surfaceTexture)
85 {
86     SurfaceTexture* const p =
87         (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
88     if (surfaceTexture.get()) {
89         surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture);
90     }
91     if (p) {
92         p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
93     }
94     env->SetLongField(thiz, fields.surfaceTexture, (jlong)surfaceTexture.get());
95 }
96 
SurfaceTexture_setProducer(JNIEnv * env,jobject thiz,const sp<IGraphicBufferProducer> & producer)97 static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz,
98         const sp<IGraphicBufferProducer>& producer)
99 {
100     IGraphicBufferProducer* const p =
101         (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
102     if (producer.get()) {
103         producer->incStrong((void*)SurfaceTexture_setProducer);
104     }
105     if (p) {
106         p->decStrong((void*)SurfaceTexture_setProducer);
107     }
108     env->SetLongField(thiz, fields.producer, (jlong)producer.get());
109 }
110 
SurfaceTexture_setFrameAvailableListener(JNIEnv * env,jobject thiz,sp<SurfaceTexture::FrameAvailableListener> listener)111 static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env,
112         jobject thiz, sp<SurfaceTexture::FrameAvailableListener> listener)
113 {
114     SurfaceTexture::FrameAvailableListener* const p =
115         (SurfaceTexture::FrameAvailableListener*)
116             env->GetLongField(thiz, fields.frameAvailableListener);
117     if (listener.get()) {
118         listener->incStrong((void*)SurfaceTexture_setSurfaceTexture);
119     }
120     if (p) {
121         p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
122     }
123     env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get());
124 }
125 
SurfaceTexture_getSurfaceTexture(JNIEnv * env,jobject thiz)126 sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
127     return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
128 }
129 
SurfaceTexture_getProducer(JNIEnv * env,jobject thiz)130 sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
131     return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
132 }
133 
android_SurfaceTexture_getNativeWindow(JNIEnv * env,jobject thiz)134 sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) {
135     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
136     sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
137     sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
138     return surfaceTextureClient;
139 }
140 
android_SurfaceTexture_isInstanceOf(JNIEnv * env,jobject thiz)141 bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
142     jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
143     return env->IsInstanceOf(thiz, surfaceTextureClass);
144 }
145 
146 // ----------------------------------------------------------------------------
147 
148 class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener
149 {
150 public:
151     JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
152     virtual ~JNISurfaceTextureContext();
153     virtual void onFrameAvailable(const BufferItem& item);
154 
155 private:
156     static JNIEnv* getJNIEnv(bool* needsDetach);
157     static void detachJNI();
158 
159     jobject mWeakThiz;
160     jclass mClazz;
161 };
162 
JNISurfaceTextureContext(JNIEnv * env,jobject weakThiz,jclass clazz)163 JNISurfaceTextureContext::JNISurfaceTextureContext(JNIEnv* env,
164         jobject weakThiz, jclass clazz) :
165     mWeakThiz(env->NewGlobalRef(weakThiz)),
166     mClazz((jclass)env->NewGlobalRef(clazz))
167 {}
168 
getJNIEnv(bool * needsDetach)169 JNIEnv* JNISurfaceTextureContext::getJNIEnv(bool* needsDetach) {
170     *needsDetach = false;
171     JNIEnv* env = AndroidRuntime::getJNIEnv();
172     if (env == NULL) {
173         JavaVMAttachArgs args = {
174             JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL };
175         JavaVM* vm = AndroidRuntime::getJavaVM();
176         int result = vm->AttachCurrentThread(&env, (void*) &args);
177         if (result != JNI_OK) {
178             ALOGE("thread attach failed: %#x", result);
179             return NULL;
180         }
181         *needsDetach = true;
182     }
183     return env;
184 }
185 
detachJNI()186 void JNISurfaceTextureContext::detachJNI() {
187     JavaVM* vm = AndroidRuntime::getJavaVM();
188     int result = vm->DetachCurrentThread();
189     if (result != JNI_OK) {
190         ALOGE("thread detach failed: %#x", result);
191     }
192 }
193 
~JNISurfaceTextureContext()194 JNISurfaceTextureContext::~JNISurfaceTextureContext()
195 {
196     bool needsDetach = false;
197     JNIEnv* env = getJNIEnv(&needsDetach);
198     if (env != NULL) {
199         env->DeleteGlobalRef(mWeakThiz);
200         env->DeleteGlobalRef(mClazz);
201     } else {
202         ALOGW("leaking JNI object references");
203     }
204     if (needsDetach) {
205         detachJNI();
206     }
207 }
208 
onFrameAvailable(const BufferItem &)209 void JNISurfaceTextureContext::onFrameAvailable(const BufferItem& /* item */)
210 {
211     bool needsDetach = false;
212     JNIEnv* env = getJNIEnv(&needsDetach);
213     if (env != NULL) {
214         env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
215     } else {
216         ALOGW("onFrameAvailable event will not posted");
217     }
218     if (needsDetach) {
219         detachJNI();
220     }
221 }
222 
223 // ----------------------------------------------------------------------------
224 
225 
226 #define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
227 #define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
228 #define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
229                                          "mFrameAvailableListener"
230 
SurfaceTexture_classInit(JNIEnv * env,jclass clazz)231 static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
232 {
233     fields.surfaceTexture = env->GetFieldID(clazz,
234             ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
235     if (fields.surfaceTexture == NULL) {
236         ALOGE("can't find android/graphics/SurfaceTexture.%s",
237                 ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
238     }
239     fields.producer = env->GetFieldID(clazz,
240             ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
241     if (fields.producer == NULL) {
242         ALOGE("can't find android/graphics/SurfaceTexture.%s",
243                 ANDROID_GRAPHICS_PRODUCER_JNI_ID);
244     }
245     fields.frameAvailableListener = env->GetFieldID(clazz,
246             ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID, "J");
247     if (fields.frameAvailableListener == NULL) {
248         ALOGE("can't find android/graphics/SurfaceTexture.%s",
249                 ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID);
250     }
251 
252     fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
253             "(Ljava/lang/ref/WeakReference;)V");
254     if (fields.postEvent == NULL) {
255         ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
256     }
257 }
258 
SurfaceTexture_init(JNIEnv * env,jobject thiz,jboolean isDetached,jint texName,jboolean singleBufferMode,jobject weakThiz)259 static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
260         jint texName, jboolean singleBufferMode, jobject weakThiz)
261 {
262     sp<IGraphicBufferProducer> producer;
263     sp<IGraphicBufferConsumer> consumer;
264     BufferQueue::createBufferQueue(&producer, &consumer);
265 
266     if (singleBufferMode) {
267         consumer->setMaxBufferCount(1);
268     }
269 
270     sp<SurfaceTexture> surfaceTexture;
271     if (isDetached) {
272         surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES,
273                 true, !singleBufferMode);
274     } else {
275         surfaceTexture = new SurfaceTexture(consumer, texName,
276                 GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
277     }
278 
279     if (surfaceTexture == 0) {
280         jniThrowException(env, OutOfResourcesException,
281                 "Unable to create native SurfaceTexture");
282         return;
283     }
284     surfaceTexture->setName(String8::format("SurfaceTexture-%d-%d-%d",
285             (isDetached ? 0 : texName),
286             getpid(),
287             createProcessUniqueId()));
288 
289     // If the current context is protected, inform the producer.
290     consumer->setConsumerIsProtected(isProtectedContext());
291 
292     SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
293     SurfaceTexture_setProducer(env, thiz, producer);
294 
295     jclass clazz = env->GetObjectClass(thiz);
296     if (clazz == NULL) {
297         jniThrowRuntimeException(env,
298                 "Can't find android/graphics/SurfaceTexture");
299         return;
300     }
301 
302     sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
303             clazz));
304     surfaceTexture->setFrameAvailableListener(ctx);
305     SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
306 }
307 
SurfaceTexture_finalize(JNIEnv * env,jobject thiz)308 static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
309 {
310     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
311     surfaceTexture->setFrameAvailableListener(0);
312     SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
313     SurfaceTexture_setSurfaceTexture(env, thiz, 0);
314     SurfaceTexture_setProducer(env, thiz, 0);
315 }
316 
SurfaceTexture_setDefaultBufferSize(JNIEnv * env,jobject thiz,jint width,jint height)317 static void SurfaceTexture_setDefaultBufferSize(
318         JNIEnv* env, jobject thiz, jint width, jint height) {
319     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
320     surfaceTexture->setDefaultBufferSize(width, height);
321 }
322 
SurfaceTexture_updateTexImage(JNIEnv * env,jobject thiz)323 static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
324 {
325     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
326     status_t err = surfaceTexture->updateTexImage();
327     if (err == INVALID_OPERATION) {
328         jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
329                 "logcat for details)");
330     } else if (err < 0) {
331         jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
332     }
333 }
334 
SurfaceTexture_releaseTexImage(JNIEnv * env,jobject thiz)335 static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz)
336 {
337     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
338     status_t err = surfaceTexture->releaseTexImage();
339     if (err == INVALID_OPERATION) {
340         jniThrowException(env, IllegalStateException, "Unable to release texture contents (see "
341                 "logcat for details)");
342     } else if (err < 0) {
343         jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
344     }
345 }
346 
SurfaceTexture_detachFromGLContext(JNIEnv * env,jobject thiz)347 static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
348 {
349     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
350     return surfaceTexture->detachFromContext();
351 }
352 
SurfaceTexture_attachToGLContext(JNIEnv * env,jobject thiz,jint tex)353 static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex)
354 {
355     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
356     return surfaceTexture->attachToContext((GLuint)tex);
357 }
358 
SurfaceTexture_getTransformMatrix(JNIEnv * env,jobject thiz,jfloatArray jmtx)359 static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
360         jfloatArray jmtx)
361 {
362     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
363     float* mtx = env->GetFloatArrayElements(jmtx, NULL);
364     surfaceTexture->getTransformMatrix(mtx);
365     env->ReleaseFloatArrayElements(jmtx, mtx, 0);
366 }
367 
SurfaceTexture_getTimestamp(JNIEnv * env,jobject thiz)368 static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
369 {
370     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
371     return surfaceTexture->getTimestamp();
372 }
373 
SurfaceTexture_release(JNIEnv * env,jobject thiz)374 static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
375 {
376     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
377     surfaceTexture->abandon();
378 }
379 
SurfaceTexture_isReleased(JNIEnv * env,jobject thiz)380 static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz)
381 {
382     sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
383     return surfaceTexture->isAbandoned();
384 }
385 
386 // ----------------------------------------------------------------------------
387 
388 static const JNINativeMethod gSurfaceTextureMethods[] = {
389     {"nativeInit",                 "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
390     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
391     {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
392     {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
393     {"nativeReleaseTexImage",      "()V",   (void*)SurfaceTexture_releaseTexImage },
394     {"nativeDetachFromGLContext",  "()I",   (void*)SurfaceTexture_detachFromGLContext },
395     {"nativeAttachToGLContext",    "(I)I",   (void*)SurfaceTexture_attachToGLContext },
396     {"nativeGetTransformMatrix",   "([F)V", (void*)SurfaceTexture_getTransformMatrix },
397     {"nativeGetTimestamp",         "()J",   (void*)SurfaceTexture_getTimestamp },
398     {"nativeRelease",              "()V",   (void*)SurfaceTexture_release },
399     {"nativeIsReleased",           "()Z",   (void*)SurfaceTexture_isReleased },
400 };
401 
register_android_graphics_SurfaceTexture(JNIEnv * env)402 int register_android_graphics_SurfaceTexture(JNIEnv* env)
403 {
404     // Cache some fields.
405     ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
406     SurfaceTexture_classInit(env, klass.get());
407 
408     return RegisterMethodsOrDie(env, kSurfaceTextureClassPathName, gSurfaceTextureMethods,
409                                 NELEM(gSurfaceTextureMethods));
410 }
411 
412 } // namespace android
413