Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/video_output/android/   (Android Betriebssystem Version 17©)  Datei vom 23.9.2026 mit Größe 37 kB image not shown  

Quelle  utils.c   Sprache: C

 

/*****************************************************************************
 * utils.c: shared code between Android vout modules.
 *****************************************************************************
 * Copyright (C) 2014-2015 VLC authors and VideoLAN
 *
 * Authors: Felix Abecassis <felix.abecassis@gmail.com>
 *          Thomas Guillem <thomas@gllm.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "utils.h"
#include "env.h"
#include <vlc_threads.h>

#include <dlfcn.h>
#include <jni.h>
#include <pthread.h>
#include <assert.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

typedef void (*ptr_ASurfaceTexture_getTransformMatrix)
                                        (ASurfaceTexture *st, float mtx[16]);
typedef ASurfaceTexture* (*ptr_ASurfaceTexture_fromSurfaceTexture)
                                        (JNIEnv* env, jobject surfacetexture);
typedef ANativeWindow* (*ptr_ASurfaceTexture_acquireANativeWindow)
                                        (ASurfaceTexture *st);
typedef jobject (*ptr_ANativeWindow_toSurface)
                                        (JNIEnv *env, ANativeWindow *window);
typedef int (*ptr_ASurfaceTexture_attachToGLContext)
                                        (ASurfaceTexture *st, uint32_t texName);
typedef int (*ptr_ASurfaceTexture_updateTexImage)(ASurfaceTexture* st);
typedef int (*ptr_ASurfaceTexture_detachFromGLContext)(ASurfaceTexture *st);
typedef void (*ptr_ASurfaceTexture_release)(ASurfaceTexture *st);

/*
 * Android SurfaceTexture handle
 */

struct vlc_asurfacetexture_priv {
    struct vlc_asurfacetexture surface;

    /* Underlying SurfaceTexture objects  (JNI and NDK)*/
    jobject         jtexture;
    ASurfaceTexture *texture;

    /* Android API are loaded into an AWindowHandler instance. */
    struct AWindowHandler *awh;
};

struct ASurfaceTextureAPI
{
    float   transMat[16];

    jobject surfacetexture;
    ASurfaceTexture *p_ast;

    ptr_ASurfaceTexture_updateTexImage pf_updateTexImage;
    ptr_ASurfaceTexture_fromSurfaceTexture pf_astFromst;
    ptr_ASurfaceTexture_attachToGLContext pf_attachToGL;
    ptr_ASurfaceTexture_detachFromGLContext pf_detachFromGL;
    ptr_ASurfaceTexture_getTransformMatrix pf_getTransMatrix;
    ptr_ASurfaceTexture_release pf_releaseAst;
    ptr_ASurfaceTexture_acquireANativeWindow pf_acquireAnw;
    ptr_ANativeWindow_toSurface pf_anwToSurface;
};

struct vlc_android_jfields
{
    struct {
        jclass clazz;
        jmethodID getVideoSurface;
        jmethodID getSubtitlesSurface;
        jmethodID registerNative;
        jmethodID unregisterNative;
        jmethodID setVideoLayout;
    } AWindow;
    struct {
          jclass clazz;
          jmethodID init_i;
          jmethodID init_iz;
          jmethodID init_z;
          jmethodID updateTexImage;
          jmethodID release;
          jmethodID releaseTexImage;
          jmethodID getTransformMatrix;
          jmethodID detachFromGLContext;
          jmethodID attachToGLContext;
    } SurfaceTexture;
    struct {
        jclass clazz;
        jmethodID init_st;
    } Surface;
};

struct AWindowHandler
{
    JavaVM *p_jvm;
    jobject jobj;
    vlc_window_t *wnd;

    struct vlc_android_jfields jfields;

    struct {
        jobject jsurface;
        ANativeWindow *p_anw;
    } views[AWindow_Max];

    void *p_anw_dl;

    struct ASurfaceTextureAPI ndk_ast_api;
    bool b_has_ndk_ast_api;

    struct {
        awh_events_t cb;
    } event;
    int capabilities;

    struct {
        jfloatArray jtransform_mtx_array;
        jfloat *jtransform_mtx;
    } stex;

    struct aimage_reader_api ndk_air_api;
    void *ndk_air_lib;
    void *ndk_sync_lib;
    bool b_has_ndk_air_api;

    struct asurface_control_api ndk_asc_api;
    bool b_has_ndk_asc_api;
};

#define JNI_CALL(what, obj, method, ...) \
    (*p_env)->what(p_env, obj, p_awh->jfields.method, ##__VA_ARGS__)
#define JNI_ANWCALL(what, method, ...) \
    (*p_env)->what(p_env, p_awh->jobj, p_awh->jfields.AWindow.method, ##__VA_ARGS__)
#define JNI_STEXCALL(what, method, ...) \
    (*p_env)->what(p_env, p_awh->jobj, p_awh->jfields.AWindow.method, ##__VA_ARGS__)

static int
NDKSurfaceTexture_attachToGLContext(
        struct vlc_asurfacetexture *surface,
        uint32_t texName)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);
    return handle->awh->ndk_ast_api.pf_attachToGL(handle->texture, texName);
}

static void
NDKSurfaceTexture_detachFromGLContext(
        struct vlc_asurfacetexture *surface)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);
    handle->awh->ndk_ast_api.pf_detachFromGL(handle->texture);
}

static int
NDKSurfaceTexture_updateTexImage(
        struct vlc_asurfacetexture *surface,
        const float **pp_transform_mtx)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    /* ASurfaceTexture_updateTexImage can fail, for example if calling it
     * before having produced a new image. */

    if (handle->awh->ndk_ast_api.pf_updateTexImage(handle->texture))
        return VLC_EGENERIC;

    handle->awh->ndk_ast_api.pf_getTransMatrix(handle->texture,
                                               handle->awh->ndk_ast_api.transMat);
    *pp_transform_mtx = handle->awh->ndk_ast_api.transMat;
    return VLC_SUCCESS;
}

static void NDKSurfaceTexture_destroy(
        struct vlc_asurfacetexture *surface)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    JNIEnv *p_env = android_getEnvCommon(NULL, handle->awh->p_jvm, "SurfaceTexture");
    if (!p_env)
        return;

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.release);

    if (handle->surface.window)
        ANativeWindow_release(handle->surface.window);

    if (handle->surface.jsurface)
        (*p_env)->DeleteGlobalRef(p_env, handle->surface.jsurface);

    handle->awh->ndk_ast_api.pf_releaseAst(handle->texture);
    (*p_env)->DeleteGlobalRef(p_env, handle->jtexture);

    free(handle);
}

static void
JNISurfaceTexture_releaseTexImage(struct vlc_asurfacetexture *surface);

static const struct vlc_asurfacetexture_operations NDKSurfaceAPI =
{
    .attach_to_gl_context = NDKSurfaceTexture_attachToGLContext,
    .update_tex_image = NDKSurfaceTexture_updateTexImage,
    .detach_from_gl_context = NDKSurfaceTexture_detachFromGLContext,
    .release_tex_image = JNISurfaceTexture_releaseTexImage,
    .destroy = NDKSurfaceTexture_destroy,
};

static int
JNISurfaceTexture_attachToGLContext(
        struct vlc_asurfacetexture *surface,
        uint32_t tex_name)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    JNIEnv *p_env = android_getEnvCommon(NULL, handle->awh->p_jvm, "SurfaceTexture");
    if (!p_env)
        return VLC_EGENERIC;

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.attachToGLContext,
                             tex_name);

    return VLC_SUCCESS;
}

static void
JNISurfaceTexture_detachFromGLContext(
        struct vlc_asurfacetexture *surface)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    AWindowHandler *p_awh = handle->awh;
    JNIEnv *p_env = android_getEnvCommon(NULL, p_awh->p_jvm, "SurfaceTexture");
    if (!p_env)
        return;

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.detachFromGLContext);

    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);
        handle->awh->stex.jtransform_mtx = NULL;
    }
}

static int
JNISurfaceTexture_updateTexImage(
        struct vlc_asurfacetexture *surface,
        const float **pp_transform_mtx)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    AWindowHandler *p_awh = handle->awh;
    JNIEnv *p_env = android_getEnvCommon(NULL, p_awh->p_jvm, "SurfaceTexture");
    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);

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.updateTexImage);

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->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_releaseTexImage(
        struct vlc_asurfacetexture *surface)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    JNIEnv *p_env = android_getEnvCommon(NULL, handle->awh->p_jvm, "SurfaceTexture");
    if (!p_env)
        return;

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.releaseTexImage);
}

static void JNISurfaceTexture_destroy(
        struct vlc_asurfacetexture *surface)
{
    struct vlc_asurfacetexture_priv *handle =
        container_of(surface, struct vlc_asurfacetexture_priv, surface);

    JNIEnv *p_env = android_getEnvCommon(NULL, handle->awh->p_jvm, "SurfaceTexture");
    if (!p_env)
        return;

    (*p_env)->CallVoidMethod(p_env, handle->jtexture,
                             handle->awh->jfields.SurfaceTexture.release);

    if (handle->surface.window)
        ANativeWindow_release(handle->surface.window);
    if (handle->surface.jsurface)
        (*p_env)->DeleteGlobalRef(p_env, handle->surface.jsurface);

    free(handle);
}

static const struct vlc_asurfacetexture_operations JNISurfaceAPI =
{
    .attach_to_gl_context = JNISurfaceTexture_attachToGLContext,
    .update_tex_image = JNISurfaceTexture_updateTexImage,
    .detach_from_gl_context = JNISurfaceTexture_detachFromGLContext,
    .release_tex_image = JNISurfaceTexture_releaseTexImage,
    .destroy = JNISurfaceTexture_destroy,
};

static int
LoadNDKSurfaceTextureAPI(AWindowHandler *p_awh, void *p_library)
{
    p_awh->ndk_ast_api.pf_astFromst = (ptr_ASurfaceTexture_fromSurfaceTexture)
        dlsym(p_library, "ASurfaceTexture_fromSurfaceTexture");
    if (p_awh->ndk_ast_api.pf_astFromst == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_updateTexImage = (ptr_ASurfaceTexture_updateTexImage)
        dlsym(p_library, "ASurfaceTexture_updateTexImage");
    if (p_awh->ndk_ast_api.pf_updateTexImage == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_attachToGL = (ptr_ASurfaceTexture_attachToGLContext)
        dlsym(p_library, "ASurfaceTexture_attachToGLContext");
    if (p_awh->ndk_ast_api.pf_attachToGL == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_detachFromGL = (ptr_ASurfaceTexture_detachFromGLContext)
        dlsym(p_library, "ASurfaceTexture_detachFromGLContext");
    if (p_awh->ndk_ast_api.pf_detachFromGL == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_getTransMatrix = (ptr_ASurfaceTexture_getTransformMatrix)
        dlsym(p_library, "ASurfaceTexture_getTransformMatrix");
    if (p_awh->ndk_ast_api.pf_getTransMatrix == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_releaseAst = (ptr_ASurfaceTexture_release)
        dlsym(p_library, "ASurfaceTexture_release");
    if (p_awh->ndk_ast_api.pf_releaseAst == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_acquireAnw = (ptr_ASurfaceTexture_acquireANativeWindow)
        dlsym(p_library, "ASurfaceTexture_acquireANativeWindow");
    if (p_awh->ndk_ast_api.pf_acquireAnw == NULL) return VLC_EGENERIC;

    p_awh->ndk_ast_api.pf_anwToSurface = (ptr_ANativeWindow_toSurface)
        dlsym(p_library, "ANativeWindow_toSurface");
    if (p_awh->ndk_ast_api.pf_anwToSurface == NULL) return VLC_EGENERIC;

    return VLC_SUCCESS;
}

static int
LoadAimageReaderAPI(AWindowHandler *p_awh, void *p_android_lib)
{
    /* AHardwareBuffer functions are in libandroid.so, API 31+ */
#define LOAD(object, name) \
    p_awh->ndk_air_api.object.name = dlsym(p_android_lib, #object "_" #name); \
    if (p_awh->ndk_air_api.object.name == NULL) return VLC_EGENERIC

    LOAD(AHardwareBuffer, getId);
    LOAD(AHardwareBuffer, describe);

#undef LOAD

    void *p_library = dlopen("libmediandk.so", RTLD_NOW);
    if (p_library == NULL)
        return VLC_EGENERIC;

#define LOAD(object, name) \
    p_awh->ndk_air_api.object.name = dlsym(p_library, #object "_" #name); \
    if (p_awh->ndk_air_api.object.name == NULL) goto error

    /* API 29+ */
    LOAD(AImageReader, newWithUsage);
    LOAD(AImageReader, delete);
    LOAD(AImageReader, getWindow);
    LOAD(AImageReader, acquireNextImageAsync);
    LOAD(AImageReader, setImageListener);

    LOAD(AImage, deleteAsync);
    LOAD(AImage, getHardwareBuffer);
    LOAD(AImage, getTimestamp);
    LOAD(AImage, getCropRect);
    LOAD(AImage, getWidth);
    LOAD(AImage, getHeight);

#undef LOAD

    /* sync_merge is in libsync.so */
    p_awh->ndk_sync_lib = dlopen("libsync.so", RTLD_NOW);
    if (p_awh->ndk_sync_lib != NULL)
    {
        p_awh->ndk_air_api.sync_merge = dlsym(p_awh->ndk_sync_lib, "sync_merge");
        if (p_awh->ndk_air_api.sync_merge == NULL)
        {
            dlclose(p_awh->ndk_sync_lib);
            p_awh->ndk_sync_lib = NULL;
            goto error;
        }
    }

    p_awh->ndk_air_lib = p_library;
    return VLC_SUCCESS;

error:
    dlclose(p_library);
    return VLC_EGENERIC;
}

static int
LoadASurfaceControlAPI(AWindowHandler *p_awh, void *p_library)
{
#define LOAD(object, name) \
    p_awh->ndk_asc_api.object.name = dlsym(p_library, #object "_" #name); \
    if (p_awh->ndk_asc_api.object.name == NULL) return VLC_EGENERIC

    /* API 31+ functions */
    LOAD(ASurfaceTransaction, setCrop);
    LOAD(ASurfaceTransaction, setPosition);
    LOAD(ASurfaceTransaction, setBufferTransform);
    LOAD(ASurfaceTransaction, setScale);
    LOAD(ASurfaceTransactionStats, getPreviousReleaseFenceFd);

    /* API 29+ functions */
    LOAD(ASurfaceControl, createFromWindow);
    LOAD(ASurfaceControl, release);
    LOAD(ASurfaceTransaction, create);
    LOAD(ASurfaceTransaction, delete);
    LOAD(ASurfaceTransaction, apply);
    LOAD(ASurfaceTransaction, setBuffer);
    LOAD(ASurfaceTransaction, setVisibility);
    LOAD(ASurfaceTransaction, setBufferTransparency);
    LOAD(ASurfaceTransaction, setBufferDataSpace);
    LOAD(ASurfaceTransaction, setHdrMetadata_smpte2086);
    LOAD(ASurfaceTransaction, setHdrMetadata_cta861_3);
    LOAD(ASurfaceTransaction, setOnComplete);
    LOAD(ASurfaceTransaction, setDesiredPresentTime);

#undef LOAD

    return VLC_SUCCESS;
}

/*
 * Android NativeWindow (post android 2.3)
 */

static void
LoadNativeWindowAPI(AWindowHandler *p_awh)
{
    void *p_library = dlopen("libandroid.so", RTLD_NOW);
    if (!p_library)
        return;

    p_awh->b_has_ndk_ast_api = LoadNDKSurfaceTextureAPI(p_awh, p_library) == VLC_SUCCESS;
    p_awh->b_has_ndk_air_api = LoadAimageReaderAPI(p_awh, p_library) == VLC_SUCCESS;
    p_awh->b_has_ndk_asc_api = LoadASurfaceControlAPI(p_awh, p_library) == VLC_SUCCESS;
    p_awh->p_anw_dl = p_library;
}

static void
AndroidNativeWindow_onMouseEvent(JNIEnv*, jobject, jlong, jint, jint, jint, jint);
static void
AndroidNativeWindow_onWindowSize(JNIEnv*, jobject, jlong, jint, jint );

const JNINativeMethod jni_callbacks[] = {
    { "nativeOnMouseEvent", "(JIIII)V",
        (void *)AndroidNativeWindow_onMouseEvent },
    { "nativeOnWindowSize", "(JII)V",
        (void *)AndroidNativeWindow_onWindowSize },
};

static int
InitJNIFields(JNIEnv *env, vlc_object_t *p_obj, jobject *jobj, AWindowHandler *awh)
{
    static vlc_mutex_t lock = VLC_STATIC_MUTEX;
    int i_init_state = -1;
    int ret;
    jclass clazz;

    vlc_mutex_lock(&lock);

    if (i_init_state != -1)
        goto end;

#define CHECK_EXCEPTION(what, critical) do { \
    if( (*env)->ExceptionCheck(env) ) \
    { \
        msg_Err(p_obj, "%s failed", what); \
        (*env)->ExceptionClear(env); \
        if (critical) { \
            i_init_state = 0; \
            goto end; \
        } \
    } \
} while( 0 )
#define GET_METHOD(id_clazz, id, str, args, critical) do { \
    awh->jfields.id_clazz.id = (*env)->GetMethodID(\
            env, awh->jfields.id_clazz.clazz, (str), (args)); \
    CHECK_EXCEPTION("GetMethodID("str")", critical); \
} while( 0 )

    if (jobj)
    {
        clazz = (*env)->GetObjectClass(env, jobj);
        CHECK_EXCEPTION("AndroidNativeWindow clazz", true);

        awh->jfields.AWindow.clazz = (*env)->NewGlobalRef(env, clazz);
        (*env)->DeleteLocalRef(env, clazz);

        GET_METHOD(AWindow, getVideoSurface,
                   "getVideoSurface", "()Landroid/view/Surface;", true);
        GET_METHOD(AWindow, getSubtitlesSurface,
                   "getSubtitlesSurface", "()Landroid/view/Surface;", true);
        GET_METHOD(AWindow, registerNative,
                   "registerNative", "(J)I", true);
        GET_METHOD(AWindow, unregisterNative,
                   "unregisterNative", "()V", true);
        GET_METHOD(AWindow, setVideoLayout,
                   "setVideoLayout", "(IIIIII)V", true);

        if ((*env)->RegisterNatives(env, awh->jfields.AWindow.clazz, jni_callbacks, 2) < 0)
        {
            msg_Err(p_obj, "RegisterNatives failed");
            i_init_state = 0;
            goto end;
        }
    }

    awh->jfields.SurfaceTexture.clazz = NULL;
    awh->jfields.Surface.clazz = NULL;

    jclass surfacetexture_class =
        (*env)->FindClass(env, "android/graphics/SurfaceTexture");
    CHECK_EXCEPTION("SurfaceTexture clazz", true);
    awh->jfields.SurfaceTexture.clazz =
        (*env)->NewGlobalRef(env, surfacetexture_class);
    (*env)->DeleteLocalRef(env, surfacetexture_class);
    if (awh->jfields.SurfaceTexture.clazz == NULL)
        goto end;

    GET_METHOD(SurfaceTexture, init_i, "<init>", "(I)V", false);
    GET_METHOD(SurfaceTexture, init_iz, "<init>", "(IZ)V", false);
    GET_METHOD(SurfaceTexture, init_z, "<init>", "(Z)V", false);

    GET_METHOD(SurfaceTexture, updateTexImage,
               "updateTexImage", "()V", true);

    GET_METHOD(SurfaceTexture, getTransformMatrix,
               "getTransformMatrix", "([F)V", true);

    GET_METHOD(SurfaceTexture, release,
               "release", "()V", true);

    GET_METHOD(SurfaceTexture, releaseTexImage,
               "releaseTexImage", "()V", false);

    GET_METHOD(SurfaceTexture, attachToGLContext,
               "attachToGLContext", "(I)V", true);

    GET_METHOD(SurfaceTexture, detachFromGLContext,
               "detachFromGLContext", "()V", true);


    /* We cannot create any SurfaceTexture if we cannot load the SurfaceTexture
     * methods. */

    if (!awh->jfields.SurfaceTexture.init_i &&
        !awh->jfields.SurfaceTexture.init_iz &&
        !awh->jfields.SurfaceTexture.init_z)
        goto error;

    jclass surface_class = (*env)->FindClass(env, "android/view/Surface");
    CHECK_EXCEPTION("android/view/Surface class", true);

    awh->jfields.Surface.clazz = (*env)->NewGlobalRef(env, surface_class);
    (*env)->DeleteLocalRef(env, surface_class);
    if (awh->jfields.Surface.clazz == NULL)
        goto error;

    GET_METHOD(Surface, init_st, "<init>",
               "(Landroid/graphics/SurfaceTexture;)V", true);

#undef GET_METHOD
#undef CHECK_EXCEPTION

    i_init_state = 1;
end:
    ret = i_init_state == 1 ? VLC_SUCCESS : VLC_EGENERIC;
    if (ret)
        msg_Err(p_obj, "AndroidNativeWindow jni init failed" );
    vlc_mutex_unlock(&lock);
    return ret;

error:
    i_init_state = 0;
    if (awh->jfields.SurfaceTexture.clazz)
        (*env)->DeleteGlobalRef(env, awh->jfields.SurfaceTexture.clazz);
    awh->jfields.SurfaceTexture.clazz = NULL;

    if (awh->jfields.Surface.clazz)
        (*env)->DeleteGlobalRef(env, awh->jfields.Surface.clazz);
    awh->jfields.Surface.clazz = NULL;

    vlc_mutex_unlock(&lock);
    msg_Err(p_obj, "Failed to load jfields table");
    return VLC_EGENERIC;
}

static JNIEnv*
AWindowHandler_getEnv(AWindowHandler *p_awh)
{
    if (p_awh->p_jvm == NULL)
        return NULL;
    return android_getEnvCommon(NULL, p_awh->p_jvm, "AWindowHandler");
}

AWindowHandler *
AWindowHandler_new(vlc_object_t *obj, vlc_window_t *wnd, awh_events_t *p_events)
{
#define AWINDOW_REGISTER_FLAGS_SUCCESS 0x1
#define AWINDOW_REGISTER_FLAGS_HAS_VIDEO_LAYOUT_LISTENER 0x2

    AWindowHandler *p_awh;
    JNIEnv *p_env;
    JavaVM *p_jvm = var_InheritAddress(obj, "android-jvm");
    jobject jobj = var_InheritAddress(obj, "drawable-androidwindow");

    if (!p_jvm)
    {
        msg_Err(obj, "libvlc_media_player options not set");
        return NULL;
    }

    p_env = android_getEnvCommon(NULL, p_jvm, "AWindowHandler");
    if (!p_env)
    {
        msg_Err(obj, "can't get JNIEnv");
        return NULL;
    }

    p_awh = calloc(1, sizeof(AWindowHandler));
    if (!p_awh)
        return NULL;

    p_awh->p_jvm = p_jvm;
    p_awh->jobj = jobj ? (*p_env)->NewGlobalRef(p_env, jobj) : NULL;

    /* Zero the jfields structure before usage. */
    p_awh->jfields = (struct vlc_android_jfields) { .AWindow.clazz = NULL };

    p_awh->wnd = wnd;
    if (p_events)
        p_awh->event.cb = *p_events;

    jfloatArray jarray = (*p_env)->NewFloatArray(p_env, 16);
    if ((*p_env)->ExceptionCheck(p_env))
    {
        (*p_env)->ExceptionClear(p_env);
        free(p_awh);
        return NULL;
    }
    p_awh->stex.jtransform_mtx_array = (*p_env)->NewGlobalRef(p_env, jarray);
    (*p_env)->DeleteLocalRef(p_env, jarray);
    p_awh->stex.jtransform_mtx = NULL;

    if (InitJNIFields(p_env, obj, jobj, p_awh) != VLC_SUCCESS)
    {
        msg_Err(obj, "InitJNIFields failed");
        return NULL;
    }

    /* If we don't have window, don't register window handler. */
    jint flags = 0;
    if (p_events != NULL && wnd != NULL)
    {
        flags = JNI_ANWCALL(CallIntMethod, registerNative,
                                       (jlong)(intptr_t)p_awh);
        if ((flags & AWINDOW_REGISTER_FLAGS_SUCCESS) == 0)
        {
            msg_Err(obj, "AWindow already registered");
            if (p_awh->jobj)
                (*p_env)->DeleteGlobalRef(p_env, p_awh->jobj);
            (*p_env)->DeleteGlobalRef(p_env, p_awh->stex.jtransform_mtx_array);
            free(p_awh);
            return NULL;
        }
    }
    LoadNativeWindowAPI(p_awh);
    msg_Dbg(obj, "has_anw: %d has_ast: %d has_air: %d has_asc: %d",
            p_awh->p_anw_dl != NULL, p_awh->b_has_ndk_ast_api,
            p_awh->b_has_ndk_air_api, p_awh->b_has_ndk_asc_api);

    p_awh->capabilities = 0;

    if (wnd && flags & AWINDOW_REGISTER_FLAGS_HAS_VIDEO_LAYOUT_LISTENER)
        p_awh->capabilities |= AWH_CAPS_SET_VIDEO_LAYOUT;

    if (p_awh->capabilities & AWH_CAPS_SET_VIDEO_LAYOUT && p_events != NULL)
    {
        /* XXX: HACK: force mediacodec to setup an OpenGL surface when the vout
         * is forced to gles2. Indeed, setting b_has_video_layout_listener to
         * false will result in mediacodec using a SurfaceTexture for output.
         */

        char *vout_modules = var_InheritString(wnd, "vout");
        if (vout_modules
         && (strncmp(vout_modules, "gles2", sizeof("gles2") - 1) == 0
          || strncmp(vout_modules, "opengles2", sizeof("opengles2") - 1) == 0))
        {
            p_awh->capabilities &= ~AWH_CAPS_SET_VIDEO_LAYOUT;
        }
        free(vout_modules);
    }

    p_awh->capabilities |= AWH_CAPS_SURFACE_VIEW;

    return p_awh;
}

AWindowHandler *
AWindowHandler_newFromANWs(vlc_object_t *obj, ANativeWindow *video,
                           ANativeWindow *subtitle)
{
    (void) obj;
    AWindowHandler *awh = calloc(1, sizeof(AWindowHandler));
    if (!awh)
        return NULL;

    awh->p_jvm = NULL;
    awh->jobj = NULL;
    awh->jfields = (struct vlc_android_jfields) { .AWindow.clazz = NULL };
    awh->wnd = NULL;

    LoadNativeWindowAPI(awh);

    awh->views[AWindow_Video].p_anw = video;
    ANativeWindow_acquire(video);
    awh->capabilities = 0;

    awh->views[AWindow_Subtitles].p_anw = subtitle;
    if (subtitle != NULL)
        ANativeWindow_acquire(subtitle);

    return awh;
}

static void
AWindowHandler_releaseANativeWindowEnv(AWindowHandler *p_awh, JNIEnv *p_env,
                                       enum AWindow_ID id)
{
    assert(id < AWindow_Max);

    if (p_awh->views[id].p_anw)
    {
        ANativeWindow_release(p_awh->views[id].p_anw);
        p_awh->views[id].p_anw = NULL;
    }

    if (p_env != NULL && p_awh->views[id].jsurface)
    {
        (*p_env)->DeleteGlobalRef(p_env, p_awh->views[id].jsurface);
        p_awh->views[id].jsurface = NULL;
    }
}

void
AWindowHandler_destroy(AWindowHandler *p_awh)
{
    JNIEnv *p_env = AWindowHandler_getEnv(p_awh);

    if (p_env)
    {
        if (p_awh->jfields.SurfaceTexture.clazz)
            (*p_env)->DeleteGlobalRef(p_env, p_awh->jfields.SurfaceTexture.clazz);

        if (p_awh->jfields.Surface.clazz)
            (*p_env)->DeleteGlobalRef(p_env, p_awh->jfields.Surface.clazz);

        if (p_awh->jobj)
            JNI_ANWCALL(CallVoidMethod, unregisterNative);
    }

    AWindowHandler_releaseANativeWindowEnv(p_awh, p_env, AWindow_Video);
    AWindowHandler_releaseANativeWindowEnv(p_awh, p_env, AWindow_Subtitles);

    if (p_env != NULL)
    {
        if (p_awh->jobj)
            (*p_env)->DeleteGlobalRef(p_env, p_awh->jobj);
        (*p_env)->DeleteGlobalRef(p_env, p_awh->stex.jtransform_mtx_array);
    }

    if (p_awh->ndk_sync_lib)
        dlclose(p_awh->ndk_sync_lib);
    if (p_awh->ndk_air_lib)
        dlclose(p_awh->ndk_air_lib);
    if (p_awh->p_anw_dl)
        dlclose(p_awh->p_anw_dl);
    free(p_awh);
}

struct aimage_reader_api *
AWindowHandler_getAImageReaderApi(AWindowHandler *p_awh)
{
    return p_awh->b_has_ndk_air_api && p_awh->b_has_ndk_asc_api ?
           &p_awh->ndk_air_api : NULL;
}

struct asurface_control_api *
AWindowHandler_getASurfaceControlApi(AWindowHandler *p_awh)
{
    return p_awh->b_has_ndk_air_api && p_awh->b_has_ndk_asc_api ?
           &p_awh->ndk_asc_api : NULL;
}

static struct vlc_asurfacetexture_priv* CreateSurfaceTexture(
        AWindowHandler *p_awh, JNIEnv *p_env, bool single_buffer)
{
    /* 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 vlc_asurfacetexture_priv *handle = malloc(sizeof *handle);
    if (handle == NULL)
        return NULL;

    handle->awh = p_awh;
    handle->texture = NULL;
    handle->jtexture = NULL;
    handle->surface.jsurface = NULL;
    handle->surface.window = NULL;
    handle->surface.ops = NULL;

    /* API 26 */
    if (p_awh->jfields.SurfaceTexture.init_z == NULL)
        goto init_iz;

    msg_Dbg(p_awh->wnd, "Using SurfaceTexture constructor init_z");

    /* We can create a SurfaceTexture in detached mode directly */
    surfacetexture = (*p_env)->NewObject(p_env,
      p_awh->jfields.SurfaceTexture.clazz, p_awh->jfields.SurfaceTexture.init_z, single_buffer);

    if (surfacetexture == NULL)
        goto error;

    goto success;

init_iz:
    msg_Dbg(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 (p_awh->jfields.SurfaceTexture.init_iz == NULL)
        goto init_i;

    msg_Dbg(p_awh->wnd, "Using SurfaceTexture constructor init_iz");
    surfacetexture = (*p_env)->NewObject(p_env,
      p_awh->jfields.SurfaceTexture.clazz, p_awh->jfields.SurfaceTexture.init_iz, texture, single_buffer);

    if (surfacetexture == NULL)
        goto error;

    goto success;

init_i:
    /* We can't get here without this constructor being loaded. */
    assert(p_awh->jfields.SurfaceTexture.init_i != NULL);
    msg_Dbg(p_awh->wnd, "Using SurfaceTexture constructor init_i");

    if (single_buffer)
        goto error;

    surfacetexture = (*p_env)->NewObject(p_env,
      p_awh->jfields.SurfaceTexture.clazz, p_awh->jfields.SurfaceTexture.init_i, texture);

    if (surfacetexture == NULL)
        goto error;

    /* fall-through success */
success:

    handle->jtexture = (*p_env)->NewGlobalRef(p_env, surfacetexture);
    (*p_env)->DeleteLocalRef(p_env, surfacetexture);

    if (handle->jtexture == NULL)
        goto error;

    if (p_awh->b_has_ndk_ast_api)
    {
        msg_Dbg(p_awh->wnd, "Using NDK API to init vlc_asurfacetexture");
        handle->texture = p_awh->ndk_ast_api.pf_astFromst(p_env, handle->jtexture);
        handle->surface.ops = &NDKSurfaceAPI;
        handle->surface.window = p_awh->ndk_ast_api.pf_acquireAnw(handle->texture);
        jobject jsurface = p_awh->ndk_ast_api.pf_anwToSurface(p_env, handle->surface.window);
        if (jsurface == NULL)
            goto error;

        handle->surface.jsurface = (*p_env)->NewGlobalRef(p_env, jsurface);
        (*p_env)->DeleteLocalRef(p_env, jsurface);

        if (handle->surface.jsurface == NULL)
            goto error;
    }
    else
    {
        msg_Dbg(p_awh->wnd, "Using JNI API to init vlc_asurfacetexture");
        handle->texture = NULL;
        /* Create Surface(SurfaceTexture), ie. producer side of the buffer
         * queue in Android. */

        jobject jsurface = (*p_env)->NewObject(p_env,
            p_awh->jfields.Surface.clazz, p_awh->jfields.Surface.init_st, handle->jtexture);
        if (!jsurface)
            goto error;
        handle->surface.jsurface = (*p_env)->NewGlobalRef(p_env, jsurface);
        (*p_env)->DeleteLocalRef(p_env, jsurface);
        if (handle->surface.jsurface == NULL)
            goto error;

        handle->surface.window =
            ANativeWindow_fromSurface(p_env, handle->surface.jsurface);
        handle->surface.ops = &JNISurfaceAPI;
    }

    msg_Dbg(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)
        ANativeWindow_release(handle->surface.window);

    if (handle->surface.jsurface != NULL)
        (*p_env)->DeleteGlobalRef(p_env, handle->surface.jsurface);

    if (handle->texture != NULL)
        p_awh->ndk_ast_api.pf_releaseAst(p_awh->ndk_ast_api.p_ast);

    if (handle->jtexture != NULL)
        (*p_env)->DeleteGlobalRef(p_env, handle->jtexture);

    free(handle);
    return NULL;
}

struct vlc_asurfacetexture *
vlc_asurfacetexture_New(AWindowHandler *p_awh, bool single_buffer)
{
    if (p_awh->p_jvm == NULL)
        return NULL;
    JNIEnv *p_env = android_getEnvCommon(NULL, p_awh->p_jvm, "SurfaceTexture");
    struct vlc_asurfacetexture_priv *surfacetexture =
        CreateSurfaceTexture(p_awh, p_env, single_buffer);
    if (surfacetexture == NULL)
        return NULL;
    return &surfacetexture->surface;
}

static int
WindowHandler_NewSurfaceEnv(AWindowHandler *p_awh, JNIEnv *p_env,
                            enum AWindow_ID id)
{
    jobject jsurface;

    switch (id)
    {
        case AWindow_Video:
            if (p_awh->wnd == NULL) return VLC_EGENERIC;
            assert(p_awh->jfields.AWindow.getVideoSurface != NULL);
            jsurface = JNI_ANWCALL(CallObjectMethod, getVideoSurface);
            break;
        case AWindow_Subtitles:
            if (p_awh->wnd == NULL) return VLC_EGENERIC;
            assert(p_awh->jfields.AWindow.getSubtitlesSurface!= NULL);
            jsurface = JNI_ANWCALL(CallObjectMethod, getSubtitlesSurface);
            break;
        default:
            vlc_assert_unreachable();
    }
    if (!jsurface)
        return VLC_EGENERIC;

    p_awh->views[id].jsurface = (*p_env)->NewGlobalRef(p_env, jsurface);
    (*p_env)->DeleteLocalRef(p_env, jsurface);
    return VLC_SUCCESS;
}

ANativeWindow *
AWindowHandler_getANativeWindow(AWindowHandler *p_awh, enum AWindow_ID id)
{
    assert(id < AWindow_Max);

    if (p_awh->views[id].p_anw)
        return p_awh->views[id].p_anw;

    JNIEnv *p_env = AWindowHandler_getEnv(p_awh);
    if (!p_env)
        return NULL;

    if (WindowHandler_NewSurfaceEnv(p_awh, p_env, id) != VLC_SUCCESS)
        return NULL;
    assert(p_awh->views[id].jsurface != NULL);

    if (!p_awh->views[id].p_anw)
        p_awh->views[id].p_anw = ANativeWindow_fromSurface(p_env,
                                                    p_awh->views[id].jsurface);

    return p_awh->views[id].p_anw;
}

void AWindowHandler_releaseANativeWindow(AWindowHandler *p_awh,
                                         enum AWindow_ID id)
{
    JNIEnv *p_env = AWindowHandler_getEnv(p_awh);
    AWindowHandler_releaseANativeWindowEnv(p_awh, p_env, id);
}

static inline AWindowHandler *jlong_AWindowHandler(jlong handle)
{
    return (AWindowHandler *)(intptr_t) handle;
}

static void
AndroidNativeWindow_onMouseEvent(JNIEnv* env, jobject clazz, jlong handle,
                                 jint action, jint button, jint x, jint y)
{
    (void) env; (void) clazz;
    AWindowHandler *p_awh = jlong_AWindowHandler(handle);

    p_awh->event.cb.on_new_mouse_coords(p_awh->wnd,
        & (struct awh_mouse_coords) { action, button, x, y });
}

static void
AndroidNativeWindow_onWindowSize(JNIEnv* env, jobject clazz, jlong handle,
                                 jint width, jint height)
{
    (void) env; (void) clazz;
    AWindowHandler *p_awh = jlong_AWindowHandler(handle);

    if (width >= 0 && height >= 0)
        p_awh->event.cb.on_new_window_size(p_awh->wnd, width, height);
}

int
AWindowHandler_getCapabilities(AWindowHandler *p_awh)
{
    return p_awh->capabilities;
}

int
AWindowHandler_setVideoLayout(AWindowHandler *p_awh,
                              int i_display_width, int i_display_height,
                              const vout_display_place_t *place)
{
    assert(p_awh->capabilities & AWH_CAPS_SET_VIDEO_LAYOUT);
    JNIEnv *p_env = AWindowHandler_getEnv(p_awh);
    if (!p_env)
        return VLC_EGENERIC;

    vout_display_place_t zero = {0};
    if (place == NULL)
        place = &zero;

    JNI_ANWCALL(CallVoidMethod, setVideoLayout, i_display_width, i_display_height,
                (int)place->width, (int)place->height, place->x, place->y);
    return VLC_SUCCESS;
}

int
SurfaceTexture_attachToGLContext(struct vlc_asurfacetexture *st, uint32_t tex_name)
{
    return st->ops->attach_to_gl_context(st, tex_name);
}

void
SurfaceTexture_detachFromGLContext(struct vlc_asurfacetexture *st)
{
    st->ops->detach_from_gl_context(st);
}

int
SurfaceTexture_updateTexImage(struct vlc_asurfacetexture *st, const float **pp_transform_mtx)
{
    return st->ops->update_tex_image(st, pp_transform_mtx);
}

void
SurfaceTexture_releaseTexImage(struct vlc_asurfacetexture *st)
{
    return st->ops->release_tex_image(st);
}

Messung
CHZG
82969892

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.