[vlc-commits] vout/android: utils: add SurfaceTexture jni helpers
Thomas Guillem
git at videolan.org
Mon Dec 19 12:06:42 CET 2016
vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Wed Dec 7 16:26:55 2016 +0100| [11faa8374d78d06119711b3ee9e9d0d5e4242c65] | committer: Thomas Guillem
vout/android: utils: add SurfaceTexture jni helpers
SurfaceTexture_waitAndUpdateTexImage() implementation could be done in JNI
because there is no proper way to implement a listener in JNI (that is needed
to be notified when a new frame is available).
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=11faa8374d78d06119711b3ee9e9d0d5e4242c65
---
modules/video_output/android/utils.c | 259 +++++++++++++++++++++++++++++++----
modules/video_output/android/utils.h | 49 +++++++
2 files changed, 283 insertions(+), 25 deletions(-)
diff --git a/modules/video_output/android/utils.c b/modules/video_output/android/utils.c
index 46646e0..d24f6cf 100644
--- a/modules/video_output/android/utils.c
+++ b/modules/video_output/android/utils.c
@@ -27,6 +27,7 @@
#include <assert.h>
typedef ANativeWindow* (*ptr_ANativeWindow_fromSurface)(JNIEnv*, jobject);
+typedef ANativeWindow* (*ptr_ANativeWindow_fromSurfaceTexture)(JNIEnv*, jobject);
typedef void (*ptr_ANativeWindow_release)(ANativeWindow*);
struct AWindowHandler
@@ -52,15 +53,37 @@ struct AWindowHandler
} event;
};
+struct SurfaceTexture
+{
+ JavaVM *p_jvm;
+
+ ptr_ANativeWindow_fromSurface pf_winFromSurface;
+ ptr_ANativeWindow_release pf_winRelease;
+
+ jobject thiz;
+ jobject jsurface;
+ ANativeWindow *p_anw;
+
+ jfloatArray jtransform_mtx_array;
+ jfloat *jtransform_mtx;
+};
+
static struct
{
struct {
+ jclass clazz;
jmethodID getVideoSurface;
jmethodID getSubtitlesSurface;
jmethodID setCallback;
jmethodID setBuffersGeometry;
jmethodID setWindowLayout;
} AndroidNativeWindow;
+ struct {
+ jmethodID create;
+ jmethodID release;
+ jmethodID waitAndUpdateTexImage;
+ jmethodID getSurface;
+ } SurfaceTextureThread;
} jfields;
/*
@@ -401,29 +424,57 @@ InitJNIFields(JNIEnv *env, vlc_object_t *p_obj, jobject *jobj)
if (i_init_state != -1)
goto end;
-#define CHECK_EXCEPTION(what) do { \
+#define CHECK_EXCEPTION(what, critical) do { \
if( (*env)->ExceptionCheck(env) ) \
{ \
msg_Err(p_obj, "%s failed", what); \
(*env)->ExceptionClear(env); \
- i_init_state = 0; \
- goto end; \
+ if (critical) { \
+ i_init_state = 0; \
+ goto end; \
+ } \
} \
} while( 0 )
-#define GET_METHOD(id, str, args) do { \
- jfields.AndroidNativeWindow.id = (*env)->GetMethodID(env, clazz, (str), (args)); \
- CHECK_EXCEPTION("GetMethodID("str")"); \
+#define GET_METHOD(id, str, args, critical) do { \
+ jfields.id = (*env)->GetMethodID(env, clazz, (str), (args)); \
+ CHECK_EXCEPTION("GetMethodID("str")", critical); \
+} while( 0 )
+#define GET_SMETHOD(id, str, args, critical) do { \
+ jfields.id = (*env)->GetStaticMethodID(env, clazz, (str), (args)); \
+ CHECK_EXCEPTION("GetMethodID("str")", critical); \
} while( 0 )
clazz = (*env)->GetObjectClass(env, jobj);
- CHECK_EXCEPTION("AndroidNativeWindow clazz");
- GET_METHOD(getVideoSurface, "getVideoSurface", "()Landroid/view/Surface;");
- GET_METHOD(getSubtitlesSurface, "getSubtitlesSurface", "()Landroid/view/Surface;");
- GET_METHOD(setCallback, "setCallback", "(J)Z");
- GET_METHOD(setBuffersGeometry, "setBuffersGeometry", "(Landroid/view/Surface;III)Z");
- GET_METHOD(setWindowLayout, "setWindowLayout", "(IIIIII)V");
-#undef CHECK_EXCEPTION
-#undef GET_METHOD
+ CHECK_EXCEPTION("AndroidNativeWindow clazz", true);
+ GET_METHOD(AndroidNativeWindow.getVideoSurface,
+ "getVideoSurface", "()Landroid/view/Surface;", true);
+ GET_METHOD(AndroidNativeWindow.getSubtitlesSurface,
+ "getSubtitlesSurface", "()Landroid/view/Surface;", true);
+ GET_METHOD(AndroidNativeWindow.setCallback,
+ "setCallback", "(J)Z", true);
+ GET_METHOD(AndroidNativeWindow.setBuffersGeometry,
+ "setBuffersGeometry", "(Landroid/view/Surface;III)Z", true);
+ GET_METHOD(AndroidNativeWindow.setWindowLayout,
+ "setWindowLayout", "(IIIIII)V", true);
+
+ GET_SMETHOD(SurfaceTextureThread.create,
+ "SurfaceTextureThread_create",
+ "(I)Lorg/videolan/libvlc/AWindow$SurfaceTextureThread;", false);
+ if (jfields.SurfaceTextureThread.create != NULL)
+ {
+ GET_SMETHOD(SurfaceTextureThread.release,
+ "SurfaceTextureThread_release",
+ "(Lorg/videolan/libvlc/AWindow$SurfaceTextureThread;)V",
+ true);
+ GET_SMETHOD(SurfaceTextureThread.waitAndUpdateTexImage,
+ "SurfaceTextureThread_waitAndUpdateTexImage",
+ "(Lorg/videolan/libvlc/AWindow$SurfaceTextureThread;[F)Z",
+ true);
+ GET_SMETHOD(SurfaceTextureThread.getSurface,
+ "SurfaceTextureThread_getSurface",
+ "(Lorg/videolan/libvlc/AWindow$SurfaceTextureThread;)"
+ "Landroid/view/Surface;", true);
+ }
if ((*env)->RegisterNatives(env, clazz, jni_callbacks, 2) < 0)
{
@@ -431,8 +482,12 @@ InitJNIFields(JNIEnv *env, vlc_object_t *p_obj, jobject *jobj)
i_init_state = 0;
goto end;
}
+ jfields.AndroidNativeWindow.clazz = (*env)->NewGlobalRef(env, clazz);
(*env)->DeleteLocalRef(env, clazz);
+#undef GET_METHOD
+#undef CHECK_EXCEPTION
+
i_init_state = 1;
msg_Dbg(p_obj, "InitJNIFields success");
end:
@@ -443,8 +498,12 @@ end:
return ret;
}
-#define JNI_CALL(what, method, ...) \
+#define JNI_CALL(what, obj, method, ...) \
+ (*p_env)->what(p_env, obj, jfields.method, ##__VA_ARGS__)
+#define JNI_ANWCALL(what, method, ...) \
(*p_env)->what(p_env, p_awh->jobj, jfields.AndroidNativeWindow.method, ##__VA_ARGS__)
+#define JNI_STEXCALL(what, method, ...) \
+ (*p_env)->what(p_env, jfields.AndroidNativeWindow.clazz, jfields.SurfaceTextureThread.method, ##__VA_ARGS__)
static JNIEnv*
AWindowHandler_getEnv(AWindowHandler *p_awh)
@@ -486,8 +545,8 @@ AWindowHandler_new(vout_window_t *wnd, awh_events_t *p_events)
LoadNativeWindowAPI(p_awh);
p_awh->wnd = wnd;
p_awh->event.cb = *p_events;
- p_awh->event.b_registered = JNI_CALL(CallBooleanMethod, setCallback,
- (jlong)(intptr_t)p_awh);
+ p_awh->event.b_registered = JNI_ANWCALL(CallBooleanMethod, setCallback,
+ (jlong)(intptr_t)p_awh);
return p_awh;
}
@@ -537,7 +596,7 @@ AWindowHandler_destroy(AWindowHandler *p_awh)
if (p_env)
{
if (p_awh->event.b_registered)
- JNI_CALL(CallBooleanMethod, setCallback, (jlong)0LL);
+ JNI_ANWCALL(CallBooleanMethod, setCallback, (jlong)0LL);
AWindowHandler_releaseANativeWindowEnv(p_awh, p_env, AWindow_Video,
false);
AWindowHandler_releaseANativeWindowEnv(p_awh, p_env, AWindow_Subtitles,
@@ -564,9 +623,9 @@ WindowHandler_NewSurfaceEnv(AWindowHandler *p_awh, JNIEnv *p_env,
jobject jsurface;
if (id == AWindow_Video)
- jsurface = JNI_CALL(CallObjectMethod, getVideoSurface);
+ jsurface = JNI_ANWCALL(CallObjectMethod, getVideoSurface);
else
- jsurface = JNI_CALL(CallObjectMethod, getSubtitlesSurface);
+ jsurface = JNI_ANWCALL(CallObjectMethod, getSubtitlesSurface);
if (!jsurface)
return VLC_EGENERIC;
@@ -659,9 +718,9 @@ AWindowHandler_setBuffersGeometry(AWindowHandler *p_awh, enum AWindow_ID id,
if (!jsurf)
return VLC_EGENERIC;
- return JNI_CALL(CallBooleanMethod, setBuffersGeometry,
- jsurf, i_width, i_height, i_format) ? VLC_SUCCESS
- : VLC_EGENERIC;
+ return JNI_ANWCALL(CallBooleanMethod, setBuffersGeometry,
+ jsurf, i_width, i_height, i_format) ? VLC_SUCCESS
+ : VLC_EGENERIC;
}
int
@@ -674,7 +733,157 @@ AWindowHandler_setWindowLayout(AWindowHandler *p_awh,
if (!p_env)
return VLC_EGENERIC;
- JNI_CALL(CallVoidMethod, setWindowLayout, i_width, i_height,
- i_visible_width,i_visible_height, i_sar_num, i_sar_den);
+ JNI_ANWCALL(CallVoidMethod, setWindowLayout, i_width, i_height,
+ i_visible_width,i_visible_height, i_sar_num, i_sar_den);
return VLC_SUCCESS;
}
+
+SurfaceTexture *
+SurfaceTexture_create(vlc_object_t *p_obj, int i_tex_name)
+{
+ if (jfields.SurfaceTextureThread.create == NULL)
+ return NULL;
+
+ JavaVM *p_jvm = var_InheritAddress(p_obj, "android-jvm");
+ if (p_jvm == NULL)
+ return NULL;
+
+ JNIEnv *p_env = android_getEnvCommon(NULL, p_jvm, "SurfaceTexture");
+ if (!p_env)
+ return NULL;
+
+ SurfaceTexture *p_stex = malloc(sizeof(SurfaceTexture));
+ if (p_stex == NULL)
+ return NULL;
+
+ /* At this point, libandroid.so should be already be loaded */
+ p_stex->pf_winFromSurface = dlsym(RTLD_DEFAULT, "ANativeWindow_fromSurface");
+ p_stex->pf_winRelease = dlsym(RTLD_DEFAULT, "ANativeWindow_release");
+ if (p_stex->pf_winFromSurface == NULL || p_stex->pf_winRelease == NULL)
+ {
+ free(p_stex);
+ return NULL;
+ }
+
+ p_stex->p_jvm = p_jvm;
+ p_stex->p_anw = NULL;
+ p_stex->jsurface = NULL;
+
+ jfloatArray jarray = (*p_env)->NewFloatArray(p_env, 16);
+ if ((*p_env)->ExceptionCheck(p_env))
+ {
+ (*p_env)->ExceptionClear(p_env);
+ goto error;
+ }
+ p_stex->jtransform_mtx_array = (*p_env)->NewGlobalRef(p_env, jarray);
+ (*p_env)->DeleteLocalRef(p_env, jarray);
+ p_stex->jtransform_mtx = NULL;
+
+ jobject thiz = JNI_STEXCALL(CallStaticObjectMethod, create, i_tex_name);
+ if ((*p_env)->ExceptionCheck(p_env))
+ {
+ (*p_env)->ExceptionClear(p_env);
+ (*p_env)->DeleteGlobalRef(p_env, p_stex->jtransform_mtx_array);
+ goto error;
+ }
+ p_stex->thiz = (*p_env)->NewGlobalRef(p_env, thiz);
+ (*p_env)->DeleteLocalRef(p_env, thiz);
+
+ return p_stex;
+
+error:
+ free(p_stex);
+ return NULL;
+}
+
+void
+SurfaceTexture_release(SurfaceTexture *p_stex)
+{
+ JNIEnv *p_env = android_getEnvCommon(NULL, p_stex->p_jvm, "SurfaceTexture");
+ if (!p_env)
+ return;
+
+ if (p_stex->p_anw != NULL)
+ p_stex->pf_winRelease(p_stex->p_anw);
+
+ if (p_stex->jsurface != NULL)
+ (*p_env)->DeleteGlobalRef(p_env, p_stex->jsurface);
+
+ JNI_STEXCALL(CallStaticVoidMethod, release, p_stex->thiz);
+ (*p_env)->DeleteGlobalRef(p_env, p_stex->thiz);
+
+ if (p_stex->jtransform_mtx != NULL)
+ (*p_env)->ReleaseFloatArrayElements(p_env, p_stex->jtransform_mtx_array,
+ p_stex->jtransform_mtx,
+ JNI_ABORT);
+ (*p_env)->DeleteGlobalRef(p_env, p_stex->jtransform_mtx_array);
+
+ free(p_stex);
+}
+
+jobject
+SurfaceTexture_getSurface(SurfaceTexture *p_stex)
+{
+ JNIEnv *p_env = android_getEnvCommon(NULL, p_stex->p_jvm, "SurfaceTexture");
+ if (!p_env)
+ return NULL;
+
+ if (p_stex->jsurface == NULL)
+ {
+ jobject jsurface =
+ JNI_STEXCALL(CallStaticObjectMethod, getSurface, p_stex->thiz);
+ if ((*p_env)->ExceptionCheck(p_env))
+ {
+ (*p_env)->ExceptionClear(p_env);
+ return NULL;
+ }
+ p_stex->jsurface = (*p_env)->NewGlobalRef(p_env, jsurface);
+ (*p_env)->DeleteLocalRef(p_env, jsurface);
+ }
+ return p_stex->jsurface;
+}
+
+ANativeWindow *
+SurfaceTexture_getANativeWindow(SurfaceTexture *p_stex)
+{
+ JNIEnv *p_env = android_getEnvCommon(NULL, p_stex->p_jvm, "SurfaceTexture");
+ if (!p_env)
+ return NULL;
+
+ if (p_stex->p_anw == NULL)
+ {
+ jobject jsurface = SurfaceTexture_getSurface(p_stex);
+ if (jsurface != NULL)
+ p_stex->p_anw = p_stex->pf_winFromSurface(p_env, jsurface);
+ }
+ return p_stex->p_anw;
+}
+
+int
+SurfaceTexture_waitAndUpdateTexImage(SurfaceTexture *p_stex,
+ const float **pp_transform_mtx)
+{
+ JNIEnv *p_env = android_getEnvCommon(NULL, p_stex->p_jvm, "SurfaceTexture");
+ if (!p_env)
+ return VLC_EGENERIC;
+
+ if (p_stex->jtransform_mtx != NULL)
+ (*p_env)->ReleaseFloatArrayElements(p_env, p_stex->jtransform_mtx_array,
+ p_stex->jtransform_mtx,
+ JNI_ABORT);
+
+ bool ret = JNI_STEXCALL(CallStaticBooleanMethod, waitAndUpdateTexImage,
+ p_stex->thiz, p_stex->jtransform_mtx_array);
+ if (ret)
+ {
+ p_stex->jtransform_mtx = (*p_env)->GetFloatArrayElements(p_env,
+ p_stex->jtransform_mtx_array, NULL);
+ *pp_transform_mtx = p_stex->jtransform_mtx;
+ return VLC_SUCCESS;
+ }
+ else
+ {
+ p_stex->jtransform_mtx = NULL;
+ return VLC_EGENERIC;
+ }
+}
diff --git a/modules/video_output/android/utils.h b/modules/video_output/android/utils.h
index 31d65e9..f4938e9 100644
--- a/modules/video_output/android/utils.h
+++ b/modules/video_output/android/utils.h
@@ -34,6 +34,7 @@
#include <vlc_common.h>
typedef struct AWindowHandler AWindowHandler;
+typedef struct SurfaceTexture SurfaceTexture;
enum AWindow_ID {
AWindow_Video,
@@ -167,3 +168,51 @@ int AWindowHandler_setWindowLayout(AWindowHandler *p_awh,
int i_width, int i_height,
int i_visible_width, int i_visible_height,
int i_sar_num, int i_sar_den);
+
+/**
+ * Construct a new Java SurfaceTexture to stream images to a given OpenGL
+ * texture
+ *
+ * See SurfaceTexture Android documentation.
+ */
+SurfaceTexture *
+SurfaceTexture_create(vlc_object_t *p_obj, int tex_name);
+
+/**
+ * Release a SurfaceTexture
+ */
+void
+SurfaceTexture_release(SurfaceTexture *p_stex);
+
+/**
+ * Get a Java Surface from the SurfaceTexture
+ *
+ * This object can be used with mediacodec_jni.
+ */
+jobject
+SurfaceTexture_getSurface(SurfaceTexture *p_stex);
+
+/**
+ * Get a ANativeWindow from the SurfaceTexture
+ *
+ * This pointer can be used with mediacodec_ndk.
+ */
+ANativeWindow *
+SurfaceTexture_getANativeWindow(SurfaceTexture *p_stex);
+
+/**
+ * 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.
+ *
+ * \param pp_transform_mtx the transform matrix fetched from
+ * SurfaceTexture.getTransformMatrix() after the
+ * SurfaceTexture.updateTexImage() call
+ * \return VLC_SUCCESS or a VLC error
+ */
+int
+SurfaceTexture_waitAndUpdateTexImage(SurfaceTexture *p_stex,
+ const float **pp_transform_mtx);
More information about the vlc-commits
mailing list