[vlc-devel] [PATCH 08/14] android: utils: extend vlc_asurfacetexture to all version

Alexandre Janniaux ajanni at videolabs.io
Fri Jun 12 11:40:51 CEST 2020


Add a JNI variant of SurfaceTexture, which supports any Android API
version starting from API 11.

It effectively removes the need for SurfaceTexture function wrapping in
the AWindow object.
---
 modules/video_output/android/utils.c | 206 +++++++++++++++++++--------
 modules/video_output/android/utils.h |   7 +-
 2 files changed, 149 insertions(+), 64 deletions(-)

diff --git a/modules/video_output/android/utils.c b/modules/video_output/android/utils.c
index 3204bc20057..f0b0f3fd64d 100644
--- a/modules/video_output/android/utils.c
+++ b/modules/video_output/android/utils.c
@@ -31,6 +31,10 @@
 #include <pthread.h>
 #include <assert.h>
 
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
 typedef ANativeWindow* (*ptr_ANativeWindow_fromSurface)(JNIEnv*, jobject);
 typedef ANativeWindow* (*ptr_ANativeWindow_fromSurfaceTexture)(JNIEnv*, jobject);
 typedef void (*ptr_ANativeWindow_release)(ANativeWindow*);
@@ -478,10 +482,11 @@ JNISurfaceTexture_attachToGLContext(
     if (!p_env)
         return VLC_EGENERIC;
 
-    AWindowHandler *p_awh = handle->awh;
+    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
+                             jfields.SurfaceTexture.attachToGLContext,
+                             tex_name);
 
-    return JNI_STEXCALL(CallBooleanMethod, attachToGLContext, tex_name) ?
-           VLC_SUCCESS : VLC_EGENERIC;
+    return VLC_SUCCESS;
 }
 
 static void
@@ -496,7 +501,8 @@ JNISurfaceTexture_detachFromGLContext(
     if (!p_env)
         return;
 
-    JNI_STEXCALL(CallVoidMethod, detachFromGLContext);
+    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
+                             jfields.SurfaceTexture.detachFromGLContext);
 
     if (handle->awh->stex.jtransform_mtx != NULL)
     {
@@ -508,7 +514,7 @@ JNISurfaceTexture_detachFromGLContext(
 }
 
 static int
-JNISurfaceTexture_waitAndUpdateTexImage(
+JNISurfaceTexture_updateTexImage(
         struct vlc_asurfacetexture *surface,
         const float **pp_transform_mtx)
 {
@@ -520,26 +526,22 @@ JNISurfaceTexture_waitAndUpdateTexImage(
     if (!p_env)
         return VLC_EGENERIC;
 
-
     if (handle->awh->stex.jtransform_mtx != NULL)
         (*p_env)->ReleaseFloatArrayElements(p_env, handle->awh->stex.jtransform_mtx_array,
                                             handle->awh->stex.jtransform_mtx,
                                             JNI_ABORT);
 
-    bool ret = JNI_STEXCALL(CallBooleanMethod, waitAndUpdateTexImage,
-                            handle->awh->stex.jtransform_mtx_array);
-    if (ret)
-    {
-        handle->awh->stex.jtransform_mtx = (*p_env)->GetFloatArrayElements(p_env,
-                                            handle->awh->stex.jtransform_mtx_array, NULL);
-        *pp_transform_mtx = handle->awh->stex.jtransform_mtx;
-        return VLC_SUCCESS;
-    }
-    else
-    {
-        handle->awh->stex.jtransform_mtx = NULL;
-        return VLC_EGENERIC;
-    }
+    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
+                             jfields.SurfaceTexture.updateTexImage);
+
+    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
+                             jfields.SurfaceTexture.getTransformMatrix,
+                             handle->awh->stex.jtransform_mtx_array);
+    handle->awh->stex.jtransform_mtx = (*p_env)->GetFloatArrayElements(p_env,
+                                        handle->awh->stex.jtransform_mtx_array, NULL);
+
+    *pp_transform_mtx = handle->awh->stex.jtransform_mtx;
+    return VLC_SUCCESS;
 }
 
 static void JNISurfaceTexture_destroy(
@@ -561,12 +563,11 @@ static void JNISurfaceTexture_destroy(
 static const struct vlc_asurfacetexture_operations JNISurfaceAPI =
 {
     .attach_to_gl_context = JNISurfaceTexture_attachToGLContext,
-    .update_tex_image = JNISurfaceTexture_waitAndUpdateTexImage,
+    .update_tex_image = JNISurfaceTexture_updateTexImage,
     .detach_from_gl_context = JNISurfaceTexture_detachFromGLContext,
     .destroy = JNISurfaceTexture_destroy,
 };
 
-
 static int
 LoadNDKSurfaceTextureAPI(AWindowHandler *p_awh, void *p_library)
 {
@@ -930,6 +931,14 @@ AWindowHandler_getANativeWindowAPI(AWindowHandler *p_awh)
 static struct SurfaceTextureHandle* SurfaceTextureHandle_Create(
         AWindowHandler *p_awh, JNIEnv *p_env)
 {
+    /* Needed in case of old API, see comments below. */
+    EGLDisplay display = EGL_NO_DISPLAY;
+    EGLSurface surface = EGL_NO_SURFACE;
+    EGLContext context = EGL_NO_CONTEXT;
+
+    EGLContext current_context = EGL_NO_CONTEXT;
+    EGLContext current_surface = EGL_NO_SURFACE;
+
     jobject surfacetexture;
 
     struct SurfaceTextureHandle *handle = malloc(sizeof *handle);
@@ -945,7 +954,7 @@ static struct SurfaceTextureHandle* SurfaceTextureHandle_Create(
 
     /* API 26 */
     if (jfields.SurfaceTexture.init_z == NULL)
-        goto error;
+        goto init_iz;
 
     msg_Info(p_awh->wnd, "Using SurfaceTexture constructor init_z");
 
@@ -956,6 +965,92 @@ static struct SurfaceTextureHandle* SurfaceTextureHandle_Create(
     if (surfacetexture == NULL)
         goto error;
 
+    goto success;
+
+init_iz:
+    msg_Info(p_awh->wnd, "Initializing OpenGL context to create SurfaceTexture");
+    /* Old Android APIs are constructing SurfaceTexture in an attached state
+     * so we need a dummy context before detaching it, for any other
+     * constructor than the previous one. That's crap.
+     * At least, Android EGL display are using reference counting so we don't
+     * need to care about display lifecycle. */
+    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    EGLint major, minor;
+    if (eglInitialize(display, &major, &minor) != EGL_TRUE)
+        goto error;
+
+    current_context = eglGetCurrentContext();
+    current_surface = eglGetCurrentSurface(EGL_READ);
+
+    static const EGLint conf_attr[] = {
+        EGL_RED_SIZE, 5,
+        EGL_GREEN_SIZE, 5,
+        EGL_BLUE_SIZE, 5,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+    EGLConfig cfgv[1];
+    EGLint cfgc;
+
+    if (eglChooseConfig(display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
+     || cfgc == 0)
+    {
+        goto error;
+    }
+
+    static const EGLint surface_attr[] =
+    {
+        EGL_WIDTH, 1,
+        EGL_HEIGHT, 1,
+        EGL_NONE,
+    };
+
+    surface = eglCreatePbufferSurface(display, cfgv[0], surface_attr);
+    if (surface == EGL_NO_SURFACE)
+        goto error;
+
+    static const EGLint context_attr[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE,
+    };
+
+    context = eglCreateContext(display, cfgv[0], EGL_NO_CONTEXT,
+                               context_attr);
+
+    eglMakeCurrent(display, surface, surface, context);
+
+    /* We'll need to reserve a name in the opengl context */
+    GLuint texture;
+    glGenTextures(1, &texture);
+
+    /* API 19 */
+    if (jfields.SurfaceTexture.init_iz == NULL)
+        goto init_i;
+
+    msg_Info(p_awh->wnd, "Using SurfaceTexture constructor init_iz");
+    surfacetexture = (*p_env)->NewObject(p_env,
+      jfields.SurfaceTexture.clazz, jfields.SurfaceTexture.init_iz, texture, false);
+
+    if (surfacetexture == NULL)
+        goto error;
+
+    goto success;
+
+init_i:
+    /* We can't get here without this constructor being loaded. */
+    assert(jfields.SurfaceTexture.init_i != NULL);
+    msg_Info(p_awh->wnd, "Using SurfaceTexture constructor init_i");
+
+    surfacetexture = (*p_env)->NewObject(p_env,
+      jfields.SurfaceTexture.clazz, jfields.SurfaceTexture.init_i, texture);
+
+    if (surfacetexture == NULL)
+        goto error;
+
+    /* fall-through success */
+success:
+
     msg_Info(p_awh->wnd, "Adding reference to surfacetexture");
     handle->jtexture = (*p_env)->NewGlobalRef(p_env, surfacetexture);
     (*p_env)->DeleteLocalRef(p_env, surfacetexture);
@@ -999,9 +1094,31 @@ static struct SurfaceTextureHandle* SurfaceTextureHandle_Create(
 
     msg_Info(p_awh->wnd, "Successfully initialized SurfaceTexture");
 
+    if (display != EGL_NO_DISPLAY)
+    {
+        handle->surface.ops->detach_from_gl_context(&handle->surface);
+        glDeleteTextures(1, &texture);
+        eglMakeCurrent(display, current_surface, current_surface, current_context);
+        eglDestroyContext(display, context);
+        eglDestroySurface(display, surface);
+        eglTerminate(display);
+    }
+
     return handle;
 
 error:
+    if (context != EGL_NO_CONTEXT)
+    {
+        glDeleteTextures(1, &texture);
+        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglDestroyContext(display, context);
+    }
+
+    if (surface != EGL_NO_SURFACE)
+        eglDestroySurface(display, surface);
+
+    if (display != EGL_NO_DISPLAY)
+        eglTerminate(display);
 
     if (handle->surface.window != NULL)
         p_awh->pf_winRelease(handle->surface.window);
@@ -1035,41 +1152,16 @@ WindowHandler_NewSurfaceEnv(AWindowHandler *p_awh, JNIEnv *p_env,
             break;
         case AWindow_SurfaceTexture:
         {
-            struct SurfaceTextureHandle *surfacetexture = NULL;
+            struct SurfaceTextureHandle *surfacetexture =
+                SurfaceTextureHandle_Create(p_awh, p_env);
 
-            if (p_awh->b_has_ndk_ast_api)
-            {
-                surfacetexture = SurfaceTextureHandle_Create(p_awh, p_env);
+            if (surfacetexture == NULL)
+                return VLC_EGENERIC;
 
-                if (surfacetexture == NULL)
-                    return VLC_EGENERIC;
-
-                surfacetexture->surface.ops = &NDKSurfaceAPI;
-                p_awh->views[id].p_anw = surfacetexture->surface.window;
-                p_awh->views[id].jsurface = surfacetexture->surface.jsurface;
-                p_awh->ndk_ast_api.p_ast = surfacetexture->texture;
-                p_awh->ndk_ast_api.surfacetexture = surfacetexture->jtexture;
-            }
-            else
-            {
-                surfacetexture = malloc(sizeof *surfacetexture);
-               /* We use AWindow wrapper functions for SurfaceTexture. */
-                jsurface = JNI_STEXCALL(CallObjectMethod, getSurface);
-                surfacetexture->surface.jsurface = (*p_env)->NewGlobalRef(p_env, jsurface);
-                (*p_env)->DeleteLocalRef(p_env, jsurface);
-                surfacetexture->surface.ops = &JNISurfaceAPI;
-                surfacetexture->surface.window
-                    = p_awh->views[id].p_anw
-                    = p_awh->pf_winFromSurface(p_env, surfacetexture->surface.jsurface);
-                surfacetexture->awh = p_awh;
-                surfacetexture->jtexture = NULL;
-                surfacetexture->texture = NULL;
-
-                p_awh->views[id].p_anw = surfacetexture->surface.window;
-                p_awh->views[id].jsurface = surfacetexture->surface.jsurface;
-                p_awh->ndk_ast_api.p_ast = NULL;
-                p_awh->ndk_ast_api.surfacetexture = NULL;
-            }
+            p_awh->views[id].p_anw = surfacetexture->surface.window;
+            p_awh->views[id].jsurface = surfacetexture->surface.jsurface;
+            p_awh->ndk_ast_api.p_ast = surfacetexture->texture;
+            p_awh->ndk_ast_api.surfacetexture = surfacetexture->jtexture;
 
             assert(surfacetexture->surface.window);
             assert(surfacetexture->surface.jsurface);
@@ -1110,8 +1202,6 @@ AWindowHandler_getANativeWindow(AWindowHandler *p_awh, enum AWindow_ID id)
     if (!p_awh->views[id].p_anw)
         p_awh->views[id].p_anw = p_awh->pf_winFromSurface(p_env,
                                                     p_awh->views[id].jsurface);
-    else
-        assert(p_awh->b_has_ndk_ast_api && id == AWindow_SurfaceTexture);
 
     return p_awh->views[id].p_anw;
 }
diff --git a/modules/video_output/android/utils.h b/modules/video_output/android/utils.h
index e8bec8b197c..e54da93a9ff 100644
--- a/modules/video_output/android/utils.h
+++ b/modules/video_output/android/utils.h
@@ -215,12 +215,7 @@ SurfaceTexture_getANativeWindow(AWindowHandler *p_awh)
 }
 
 /**
- * Wait for a new frame and update it
- *
- * This function must be called from the OpenGL thread. This is an helper that
- * waits for a new frame via the Java SurfaceTexture.OnFrameAvailableListener
- * listener and update the frame via the SurfaceTexture.updateTexImage()
- * method.
+ * Update the SurfaceTexture to the most recent frame.
  *
  * \param pp_transform_mtx the transform matrix fetched from
  * SurfaceTexture.getTransformMatrix() after the
-- 
2.27.0



More information about the vlc-devel mailing list