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