SurfaceTexture.cpp revision c7282e57cd01f1576baac04356bf99bee34e4c18
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/view/Surface$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  producer;
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->GetLongField(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->SetLongField(thiz, fields.surfaceTexture, (jlong)surfaceTexture.get());
66}
67
68static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz,
69        const sp<IGraphicBufferProducer>& producer)
70{
71    IGraphicBufferProducer* const p =
72        (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
73    if (producer.get()) {
74        producer->incStrong((void*)SurfaceTexture_setProducer);
75    }
76    if (p) {
77        p->decStrong((void*)SurfaceTexture_setProducer);
78    }
79    env->SetLongField(thiz, fields.producer, (jlong)producer.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->GetLongField(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->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get());
95}
96
97sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
98    return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture);
99}
100
101sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
102    return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
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 = {
145            JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL };
146        JavaVM* vm = AndroidRuntime::getJavaVM();
147        int result = vm->AttachCurrentThread(&env, (void*) &args);
148        if (result != JNI_OK) {
149            ALOGE("thread attach failed: %#x", result);
150            return NULL;
151        }
152        *needsDetach = true;
153    }
154    return env;
155}
156
157void JNISurfaceTextureContext::detachJNI() {
158    JavaVM* vm = AndroidRuntime::getJavaVM();
159    int result = vm->DetachCurrentThread();
160    if (result != JNI_OK) {
161        ALOGE("thread detach failed: %#x", result);
162    }
163}
164
165JNISurfaceTextureContext::~JNISurfaceTextureContext()
166{
167    bool needsDetach = false;
168    JNIEnv* env = getJNIEnv(&needsDetach);
169    if (env != NULL) {
170        env->DeleteGlobalRef(mWeakThiz);
171        env->DeleteGlobalRef(mClazz);
172    } else {
173        ALOGW("leaking JNI object references");
174    }
175    if (needsDetach) {
176        detachJNI();
177    }
178}
179
180void JNISurfaceTextureContext::onFrameAvailable()
181{
182    bool needsDetach = false;
183    JNIEnv* env = getJNIEnv(&needsDetach);
184    if (env != NULL) {
185        env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
186    } else {
187        ALOGW("onFrameAvailable event will not posted");
188    }
189    if (needsDetach) {
190        detachJNI();
191    }
192}
193
194// ----------------------------------------------------------------------------
195
196
197#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
198#define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
199#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
200                                         "mFrameAvailableListener"
201
202static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
203{
204    fields.surfaceTexture = env->GetFieldID(clazz,
205            ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
206    if (fields.surfaceTexture == NULL) {
207        ALOGE("can't find android/graphics/SurfaceTexture.%s",
208                ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
209    }
210    fields.producer = env->GetFieldID(clazz,
211            ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
212    if (fields.producer == NULL) {
213        ALOGE("can't find android/graphics/SurfaceTexture.%s",
214                ANDROID_GRAPHICS_PRODUCER_JNI_ID);
215    }
216    fields.frameAvailableListener = env->GetFieldID(clazz,
217            ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID, "J");
218    if (fields.frameAvailableListener == NULL) {
219        ALOGE("can't find android/graphics/SurfaceTexture.%s",
220                ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID);
221    }
222
223    fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
224            "(Ljava/lang/ref/WeakReference;)V");
225    if (fields.postEvent == NULL) {
226        ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
227    }
228}
229
230static void SurfaceTexture_init(JNIEnv* env, jobject thiz,
231        jint texName, jboolean singleBufferMode, jobject weakThiz)
232{
233    sp<IGraphicBufferProducer> producer;
234    sp<IGraphicBufferConsumer> consumer;
235    BufferQueue::createBufferQueue(&producer, &consumer);
236
237    if (singleBufferMode) {
238        consumer->disableAsyncBuffer();
239        consumer->setDefaultMaxBufferCount(1);
240    }
241
242    sp<GLConsumer> surfaceTexture(new GLConsumer(consumer, texName,
243            GL_TEXTURE_EXTERNAL_OES, true, true));
244    if (surfaceTexture == 0) {
245        jniThrowException(env, OutOfResourcesException,
246                "Unable to create native SurfaceTexture");
247        return;
248    }
249    SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
250    SurfaceTexture_setProducer(env, thiz, producer);
251
252    jclass clazz = env->GetObjectClass(thiz);
253    if (clazz == NULL) {
254        jniThrowRuntimeException(env,
255                "Can't find android/graphics/SurfaceTexture");
256        return;
257    }
258
259    sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
260            clazz));
261    surfaceTexture->setFrameAvailableListener(ctx);
262    SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
263}
264
265static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
266{
267    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
268    surfaceTexture->setFrameAvailableListener(0);
269    SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
270    SurfaceTexture_setSurfaceTexture(env, thiz, 0);
271    SurfaceTexture_setProducer(env, thiz, 0);
272}
273
274static void SurfaceTexture_setDefaultBufferSize(
275        JNIEnv* env, jobject thiz, jint width, jint height) {
276    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
277    surfaceTexture->setDefaultBufferSize(width, height);
278}
279
280static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
281{
282    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
283    status_t err = surfaceTexture->updateTexImage();
284    if (err == INVALID_OPERATION) {
285        jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
286                "logcat for details)");
287    } else if (err < 0) {
288        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
289    }
290}
291
292static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz)
293{
294    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
295    status_t err = surfaceTexture->releaseTexImage();
296    if (err == INVALID_OPERATION) {
297        jniThrowException(env, IllegalStateException, "Unable to release texture contents (see "
298                "logcat for details)");
299    } else if (err < 0) {
300        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
301    }
302}
303
304static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
305{
306    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
307    return surfaceTexture->detachFromContext();
308}
309
310static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex)
311{
312    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
313    return surfaceTexture->attachToContext((GLuint)tex);
314}
315
316static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
317        jfloatArray jmtx)
318{
319    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
320    float* mtx = env->GetFloatArrayElements(jmtx, NULL);
321    surfaceTexture->getTransformMatrix(mtx);
322    env->ReleaseFloatArrayElements(jmtx, mtx, 0);
323}
324
325static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
326{
327    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
328    return surfaceTexture->getTimestamp();
329}
330
331static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
332{
333    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
334    surfaceTexture->abandon();
335}
336
337// ----------------------------------------------------------------------------
338
339static JNINativeMethod gSurfaceTextureMethods[] = {
340    {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
341    {"nativeInit",                 "(IZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
342    {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
343    {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
344    {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
345    {"nativeReleaseTexImage",      "()V",   (void*)SurfaceTexture_releaseTexImage },
346    {"nativeDetachFromGLContext",  "()I",   (void*)SurfaceTexture_detachFromGLContext },
347    {"nativeAttachToGLContext",    "(I)I",   (void*)SurfaceTexture_attachToGLContext },
348    {"nativeGetTransformMatrix",   "([F)V", (void*)SurfaceTexture_getTransformMatrix },
349    {"nativeGetTimestamp",         "()J",   (void*)SurfaceTexture_getTimestamp },
350    {"nativeRelease",              "()V",   (void*)SurfaceTexture_release },
351};
352
353int register_android_graphics_SurfaceTexture(JNIEnv* env)
354{
355    int err = 0;
356    err = AndroidRuntime::registerNativeMethods(env, kSurfaceTextureClassPathName,
357            gSurfaceTextureMethods, NELEM(gSurfaceTextureMethods));
358    return err;
359}
360
361} // namespace android
362