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