SurfaceTexture.cpp revision e3eae73abb5e429d9747f9c36ab025219e68bb26
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 <GLES2/gl2.h>
22#include <GLES2/gl2ext.h>
23
24#include <gui/GLConsumer.h>
25#include <gui/Surface.h>
26
27#include <android_runtime/AndroidRuntime.h>
28
29#include <utils/Log.h>
30#include <utils/misc.h>
31
32#include "jni.h"
33#include "JNIHelp.h"
34
35// ----------------------------------------------------------------------------
36
37namespace android {
38
39static const char* const OutOfResourcesException =
40    "android/graphics/SurfaceTexture$OutOfResourcesException";
41static const char* const IllegalStateException = "java/lang/IllegalStateException";
42const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
43
44struct fields_t {
45    jfieldID  surfaceTexture;
46    jfieldID  bufferQueue;
47    jfieldID  frameAvailableListener;
48    jmethodID postEvent;
49};
50static fields_t fields;
51
52// ----------------------------------------------------------------------------
53
54static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz,
55        const sp<GLConsumer>& surfaceTexture)
56{
57    GLConsumer* const p =
58        (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture);
59    if (surfaceTexture.get()) {
60        surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture);
61    }
62    if (p) {
63        p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
64    }
65    env->SetIntField(thiz, fields.surfaceTexture, (int)surfaceTexture.get());
66}
67
68static void SurfaceTexture_setBufferQueue(JNIEnv* env, jobject thiz,
69        const sp<BufferQueue>& bq)
70{
71    BufferQueue* const p =
72        (BufferQueue*)env->GetIntField(thiz, fields.bufferQueue);
73    if (bq.get()) {
74        bq->incStrong((void*)SurfaceTexture_setBufferQueue);
75    }
76    if (p) {
77        p->decStrong((void*)SurfaceTexture_setBufferQueue);
78    }
79    env->SetIntField(thiz, fields.bufferQueue, (int)bq.get());
80}
81
82static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env,
83        jobject thiz, sp<GLConsumer::FrameAvailableListener> listener)
84{
85    GLConsumer::FrameAvailableListener* const p =
86        (GLConsumer::FrameAvailableListener*)
87            env->GetIntField(thiz, fields.frameAvailableListener);
88    if (listener.get()) {
89        listener->incStrong((void*)SurfaceTexture_setSurfaceTexture);
90    }
91    if (p) {
92        p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
93    }
94    env->SetIntField(thiz, fields.frameAvailableListener, (int)listener.get());
95}
96
97sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
98    return (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture);
99}
100
101sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
102    return (BufferQueue*)env->GetIntField(thiz, fields.bufferQueue);
103}
104
105sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) {
106    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
107    sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
108    sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
109    return surfaceTextureClient;
110}
111
112bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
113    jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
114    return env->IsInstanceOf(thiz, surfaceTextureClass);
115}
116
117// ----------------------------------------------------------------------------
118
119class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener
120{
121public:
122    JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
123    virtual ~JNISurfaceTextureContext();
124    virtual void onFrameAvailable();
125
126private:
127    static JNIEnv* getJNIEnv(bool* needsDetach);
128    static void detachJNI();
129
130    jobject mWeakThiz;
131    jclass mClazz;
132};
133
134JNISurfaceTextureContext::JNISurfaceTextureContext(JNIEnv* env,
135        jobject weakThiz, jclass clazz) :
136    mWeakThiz(env->NewGlobalRef(weakThiz)),
137    mClazz((jclass)env->NewGlobalRef(clazz))
138{}
139
140JNIEnv* JNISurfaceTextureContext::getJNIEnv(bool* needsDetach) {
141    *needsDetach = false;
142    JNIEnv* env = AndroidRuntime::getJNIEnv();
143    if (env == NULL) {
144        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
145        JavaVM* vm = AndroidRuntime::getJavaVM();
146        int result = vm->AttachCurrentThread(&env, (void*) &args);
147        if (result != JNI_OK) {
148            ALOGE("thread attach failed: %#x", result);
149            return NULL;
150        }
151        *needsDetach = true;
152    }
153    return env;
154}
155
156void JNISurfaceTextureContext::detachJNI() {
157    JavaVM* vm = AndroidRuntime::getJavaVM();
158    int result = vm->DetachCurrentThread();
159    if (result != JNI_OK) {
160        ALOGE("thread detach failed: %#x", result);
161    }
162}
163
164JNISurfaceTextureContext::~JNISurfaceTextureContext()
165{
166    bool needsDetach = false;
167    JNIEnv* env = getJNIEnv(&needsDetach);
168    if (env != NULL) {
169        env->DeleteGlobalRef(mWeakThiz);
170        env->DeleteGlobalRef(mClazz);
171    } else {
172        ALOGW("leaking JNI object references");
173    }
174    if (needsDetach) {
175        detachJNI();
176    }
177}
178
179void JNISurfaceTextureContext::onFrameAvailable()
180{
181    bool needsDetach = false;
182    JNIEnv* env = getJNIEnv(&needsDetach);
183    if (env != NULL) {
184        env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
185    } else {
186        ALOGW("onFrameAvailable event will not posted");
187    }
188    if (needsDetach) {
189        detachJNI();
190    }
191}
192
193// ----------------------------------------------------------------------------
194
195
196#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
197#define ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID "mBufferQueue"
198#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
199                                         "mFrameAvailableListener"
200
201static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
202{
203    fields.surfaceTexture = env->GetFieldID(clazz,
204            ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "I");
205    if (fields.surfaceTexture == NULL) {
206        ALOGE("can't find android/graphics/SurfaceTexture.%s",
207                ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
208    }
209    fields.bufferQueue = env->GetFieldID(clazz,
210            ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID, "I");
211    if (fields.bufferQueue == NULL) {
212        ALOGE("can't find android/graphics/SurfaceTexture.%s",
213                ANDROID_GRAPHICS_BUFFERQUEUE_JNI_ID);
214    }
215    fields.frameAvailableListener = env->GetFieldID(clazz,
216            ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID, "I");
217    if (fields.frameAvailableListener == NULL) {
218        ALOGE("can't find android/graphics/SurfaceTexture.%s",
219                ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID);
220    }
221
222    fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
223            "(Ljava/lang/Object;)V");
224    if (fields.postEvent == NULL) {
225        ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
226    }
227}
228
229static void SurfaceTexture_init(JNIEnv* env, jobject thiz,
230        jint texName, jboolean singleBufferMode, jobject weakThiz)
231{
232    sp<BufferQueue> bq = new BufferQueue();
233
234    if (singleBufferMode) {
235        bq->disableAsyncBuffer();
236        bq->setDefaultMaxBufferCount(1);
237    }
238
239    sp<GLConsumer> surfaceTexture(new GLConsumer(bq, texName, GL_TEXTURE_EXTERNAL_OES, true, true));
240    if (surfaceTexture == 0) {
241        jniThrowException(env, OutOfResourcesException,
242                "Unable to create native SurfaceTexture");
243        return;
244    }
245    SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
246    SurfaceTexture_setBufferQueue(env, thiz, bq);
247
248    jclass clazz = env->GetObjectClass(thiz);
249    if (clazz == NULL) {
250        jniThrowRuntimeException(env,
251                "Can't find android/graphics/SurfaceTexture");
252        return;
253    }
254
255    sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
256            clazz));
257    surfaceTexture->setFrameAvailableListener(ctx);
258    SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
259}
260
261static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
262{
263    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
264    surfaceTexture->setFrameAvailableListener(0);
265    SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
266    SurfaceTexture_setSurfaceTexture(env, thiz, 0);
267    SurfaceTexture_setBufferQueue(env, thiz, 0);
268}
269
270static void SurfaceTexture_setDefaultBufferSize(
271        JNIEnv* env, jobject thiz, jint width, jint height) {
272    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
273    surfaceTexture->setDefaultBufferSize(width, height);
274}
275
276static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
277{
278    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
279    status_t err = surfaceTexture->updateTexImage();
280    if (err == INVALID_OPERATION) {
281        jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
282                "logcat for details)");
283    } else if (err < 0) {
284        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
285    }
286}
287
288static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz)
289{
290    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
291    status_t err = surfaceTexture->releaseTexImage();
292    if (err == INVALID_OPERATION) {
293        jniThrowException(env, IllegalStateException, "Unable to release texture contents (see "
294                "logcat for details)");
295    } else if (err < 0) {
296        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
297    }
298}
299
300static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
301{
302    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
303    return surfaceTexture->detachFromContext();
304}
305
306static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex)
307{
308    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
309    return surfaceTexture->attachToContext((GLuint)tex);
310}
311
312static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
313        jfloatArray jmtx)
314{
315    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
316    float* mtx = env->GetFloatArrayElements(jmtx, NULL);
317    surfaceTexture->getTransformMatrix(mtx);
318    env->ReleaseFloatArrayElements(jmtx, mtx, 0);
319}
320
321static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
322{
323    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
324    return surfaceTexture->getTimestamp();
325}
326
327static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
328{
329    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
330    surfaceTexture->abandon();
331}
332
333// ----------------------------------------------------------------------------
334
335static JNINativeMethod gSurfaceTextureMethods[] = {
336    {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
337    {"nativeInit",                 "(IZLjava/lang/Object;)V", (void*)SurfaceTexture_init },
338    {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
339    {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
340    {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
341    {"nativeReleaseTexImage",      "()V",   (void*)SurfaceTexture_releaseTexImage },
342    {"nativeDetachFromGLContext",  "()I",   (void*)SurfaceTexture_detachFromGLContext },
343    {"nativeAttachToGLContext",    "(I)I",   (void*)SurfaceTexture_attachToGLContext },
344    {"nativeGetTransformMatrix",   "([F)V", (void*)SurfaceTexture_getTransformMatrix },
345    {"nativeGetTimestamp",         "()J",   (void*)SurfaceTexture_getTimestamp },
346    {"nativeRelease",              "()V",   (void*)SurfaceTexture_release },
347};
348
349int register_android_graphics_SurfaceTexture(JNIEnv* env)
350{
351    int err = 0;
352    err = AndroidRuntime::registerNativeMethods(env, kSurfaceTextureClassPathName,
353            gSurfaceTextureMethods, NELEM(gSurfaceTextureMethods));
354    return err;
355}
356
357} // namespace android
358