SurfaceTexture.cpp revision afe9a4c67c2e2988c76232796f017a992a6f2a28
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
41namespace android {
42
43static const char* const OutOfResourcesException =
44    "android/view/Surface$OutOfResourcesException";
45static const char* const IllegalStateException = "java/lang/IllegalStateException";
46const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
47
48struct fields_t {
49    jfieldID  surfaceTexture;
50    jfieldID  producer;
51    jfieldID  frameAvailableListener;
52    jmethodID postEvent;
53};
54static fields_t fields;
55
56// Get an ID that's unique within this process.
57static 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.
63static 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
79static 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
93static 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
107static 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
122sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
123    return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture);
124}
125
126sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
127    return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
128}
129
130sp<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
137bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
138    jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
139    return env->IsInstanceOf(thiz, surfaceTextureClass);
140}
141
142// ----------------------------------------------------------------------------
143
144class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener
145{
146public:
147    JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
148    virtual ~JNISurfaceTextureContext();
149    virtual void onFrameAvailable(const BufferItem& item);
150
151private:
152    static JNIEnv* getJNIEnv(bool* needsDetach);
153    static void detachJNI();
154
155    jobject mWeakThiz;
156    jclass mClazz;
157};
158
159JNISurfaceTextureContext::JNISurfaceTextureContext(JNIEnv* env,
160        jobject weakThiz, jclass clazz) :
161    mWeakThiz(env->NewGlobalRef(weakThiz)),
162    mClazz((jclass)env->NewGlobalRef(clazz))
163{}
164
165JNIEnv* 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
182void 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
190JNISurfaceTextureContext::~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
205void 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
227static 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
255static 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
306static 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
315static 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
321static 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
333static 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
345static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
346{
347    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
348    return surfaceTexture->detachFromContext();
349}
350
351static 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
357static 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
366static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
367{
368    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
369    return surfaceTexture->getTimestamp();
370}
371
372static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
373{
374    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
375    surfaceTexture->abandon();
376}
377
378static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz)
379{
380    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
381    return surfaceTexture->isAbandoned();
382}
383
384// ----------------------------------------------------------------------------
385
386static 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
401int register_android_graphics_SurfaceTexture(JNIEnv* env)
402{
403    return RegisterMethodsOrDie(env, kSurfaceTextureClassPathName, gSurfaceTextureMethods,
404                                NELEM(gSurfaceTextureMethods));
405}
406
407} // namespace android
408