[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