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