SurfaceTexture.cpp revision 493f2e11909e2d5839ca81ddc66d48d538192478
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, jboolean isDetached,
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;
243    if (isDetached) {
244        surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
245                true, true);
246    } else {
247        surfaceTexture = new GLConsumer(consumer, texName,
248                GL_TEXTURE_EXTERNAL_OES, true, true);
249    }
250
251    if (surfaceTexture == 0) {
252        jniThrowException(env, OutOfResourcesException,
253                "Unable to create native SurfaceTexture");
254        return;
255    }
256    SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
257    SurfaceTexture_setProducer(env, thiz, producer);
258
259    jclass clazz = env->GetObjectClass(thiz);
260    if (clazz == NULL) {
261        jniThrowRuntimeException(env,
262                "Can't find android/graphics/SurfaceTexture");
263        return;
264    }
265
266    sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
267            clazz));
268    surfaceTexture->setFrameAvailableListener(ctx);
269    SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
270}
271
272static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
273{
274    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
275    surfaceTexture->setFrameAvailableListener(0);
276    SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
277    SurfaceTexture_setSurfaceTexture(env, thiz, 0);
278    SurfaceTexture_setProducer(env, thiz, 0);
279}
280
281static void SurfaceTexture_setDefaultBufferSize(
282        JNIEnv* env, jobject thiz, jint width, jint height) {
283    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
284    surfaceTexture->setDefaultBufferSize(width, height);
285}
286
287static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
288{
289    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
290    status_t err = surfaceTexture->updateTexImage();
291    if (err == INVALID_OPERATION) {
292        jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
293                "logcat for details)");
294    } else if (err < 0) {
295        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
296    }
297}
298
299static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz)
300{
301    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
302    status_t err = surfaceTexture->releaseTexImage();
303    if (err == INVALID_OPERATION) {
304        jniThrowException(env, IllegalStateException, "Unable to release texture contents (see "
305                "logcat for details)");
306    } else if (err < 0) {
307        jniThrowRuntimeException(env, "Error during updateTexImage (see logcat for details)");
308    }
309}
310
311static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
312{
313    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
314    return surfaceTexture->detachFromContext();
315}
316
317static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex)
318{
319    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
320    return surfaceTexture->attachToContext((GLuint)tex);
321}
322
323static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
324        jfloatArray jmtx)
325{
326    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
327    float* mtx = env->GetFloatArrayElements(jmtx, NULL);
328    surfaceTexture->getTransformMatrix(mtx);
329    env->ReleaseFloatArrayElements(jmtx, mtx, 0);
330}
331
332static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
333{
334    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
335    return surfaceTexture->getTimestamp();
336}
337
338static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
339{
340    sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
341    surfaceTexture->abandon();
342}
343
344// ----------------------------------------------------------------------------
345
346static JNINativeMethod gSurfaceTextureMethods[] = {
347    {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
348    {"nativeInit",                 "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
349    {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
350    {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
351    {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
352    {"nativeReleaseTexImage",      "()V",   (void*)SurfaceTexture_releaseTexImage },
353    {"nativeDetachFromGLContext",  "()I",   (void*)SurfaceTexture_detachFromGLContext },
354    {"nativeAttachToGLContext",    "(I)I",   (void*)SurfaceTexture_attachToGLContext },
355    {"nativeGetTransformMatrix",   "([F)V", (void*)SurfaceTexture_getTransformMatrix },
356    {"nativeGetTimestamp",         "()J",   (void*)SurfaceTexture_getTimestamp },
357    {"nativeRelease",              "()V",   (void*)SurfaceTexture_release },
358};
359
360int register_android_graphics_SurfaceTexture(JNIEnv* env)
361{
362    int err = 0;
363    err = AndroidRuntime::registerNativeMethods(env, kSurfaceTextureClassPathName,
364            gSurfaceTextureMethods, NELEM(gSurfaceTextureMethods));
365    return err;
366}
367
368} // namespace android
369