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

Thomas Guillem thomas at gllm.fr
Mon Jun 15 13:21:40 CEST 2020



On Fri, Jun 12, 2020, at 11:40, Alexandre Janniaux wrote:
> 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;
> +

Maybe, put it in a specific function ?

> +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
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list