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