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