You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1210 lines
38 KiB
1210 lines
38 KiB
/*****************************************************************************
|
|
* 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);
|
|
}
|
|
|