...
 
Commits (4)
  • Thomas Guillem's avatar
    Update VLC HASH · b69c666b
    Thomas Guillem authored
    b69c666b
  • Victorien Le Couviour--Tuffet's avatar
    build: fix gas dependency · c1d909dd
    Victorien Le Couviour--Tuffet authored
    c1d909dd
  • Thomas Guillem's avatar
    build: restore c++_shared usage · b69c0825
    Thomas Guillem authored
    This is a partial revert of the following commits
     - b775dbba "build: fix ODR violation"
     - b9df5331 "Use libc++ as static library"
    
    UnsatisfiedLinkError with c++_shared should be fixed since we now use the last
    NDK version and we dropped old android versions that triggered this crash.
    
    This commit remove the ugly jniloader hack.
    
    Here are the list of .so that are now shipped within the APK:
     - c++_shared.so (from the NDK)
     - libvlc.so (all VLC modules, VLC/LibVLC symbols) => link with c++_shared.so
     - libvlcjni.so (all JNI symbols) => link with libvlc.so
     - libmla.so (medialibrary, optional)  => link with libvlc.so and c++_shared.so
    
    The APK size won't change with this commit (the difference of .so sizes is
    negligible).
    b69c0825
  • Thomas Guillem's avatar
    MediaPlayer: add GLRenderer · 7a513663
    Thomas Guillem authored
    Android Java helper for the libvlc_video_set_opengl_callbacks() function.
    7a513663
......@@ -7,6 +7,9 @@ medialibrary/medialibrary
medialibrary/sqlite*
medialibrary/libvlcpp
medialibrary/build
medialibrary/jni/obj
medialibrary/jni/libs
medialibrary/.libs
# Android .so
/android-libs/**/*.so
......
......@@ -403,7 +403,7 @@ elif [ "${PLATFORM_SHORT_ARCH}" = "arm" ]; then
NDK_LIB_DIR="${NDK_LIB_DIR}/armv7-a"
fi
EXTRA_LDFLAGS="${EXTRA_LDFLAGS} -L${NDK_LIB_DIR} -lc++abi -lc++_static"
EXTRA_LDFLAGS="${EXTRA_LDFLAGS} -L${NDK_LIB_DIR} -lc++abi"
VLC_LDFLAGS="${EXTRA_LDFLAGS}"
# Release or not?
......@@ -439,7 +439,7 @@ cd extras/tools
checkfail "buildsystem tools: bootstrap failed"
make $MAKEFLAGS
checkfail "buildsystem tools: make failed"
make $MAKEFLAGS .gas
make $MAKEFLAGS .buildgas
checkfail "buildsystem tools: make failed"
cd ../..
......@@ -669,6 +669,26 @@ rm ${REDEFINED_VLC_MODULES_DIR}/syms
VLC_MODULES=$(find_modules ${REDEFINED_VLC_MODULES_DIR})
VLC_CONTRIB_LDFLAGS=`for i in $(/bin/ls $VLC_CONTRIB/lib/pkgconfig/*.pc); do PKG_CONFIG_PATH="$VLC_CONTRIB/lib/pkgconfig/" pkg-config --libs $i; done |xargs`
echo -e "ndk-build vlc"
$ANDROID_NDK/ndk-build$OSCMD -C libvlc \
APP_STL="c++_shared" \
APP_CPPFLAGS="-frtti -fexceptions" \
VLC_SRC_DIR="$VLC_SRC_DIR" \
VLC_BUILD_DIR="$VLC_SRC_DIR/$VLC_BUILD_DIR" \
VLC_CONTRIB="$VLC_CONTRIB" \
VLC_CONTRIB_LDFLAGS="$VLC_CONTRIB_LDFLAGS" \
VLC_MODULES="$VLC_MODULES" \
VLC_LDFLAGS="$VLC_LDFLAGS -latomic" \
APP_BUILD_SCRIPT=jni/Android.mk \
APP_PLATFORM=android-${ANDROID_API} \
APP_ABI=${ANDROID_ABI} \
NDK_PROJECT_PATH=jni \
NDK_TOOLCHAIN_VERSION=clang \
NDK_DEBUG=${NDK_DEBUG} \
BUILD_ML=${BUILD_ML}
checkfail "ndk-build libvlc failed"
################
# MEDIALIBRARY #
......@@ -809,51 +829,29 @@ checkfail "medialibrary: make failed"
cd ${SRC_DIR}
MEDIALIBRARY_LDLIBS="-L${MEDIALIBRARY_BUILD_DIR}/build-android-$ANDROID_ABI/.libs -lmedialibrary \
MEDIALIBRARY_LDLIBS="-L$SRC_DIR/libvlc/jni/libs/$ANDROID_ABI -lvlc \
-L${MEDIALIBRARY_BUILD_DIR}/build-android-$ANDROID_ABI/.libs -lmedialibrary \
-L$SRC_DIR/vlc/contrib/contrib-android-$TARGET_TUPLE/jpeg/.libs -ljpeg \
-L$MEDIALIBRARY_MODULE_DIR/$SQLITE_RELEASE/build-$ANDROID_ABI/.libs -lsqlite3"
-L$MEDIALIBRARY_MODULE_DIR/$SQLITE_RELEASE/build-$ANDROID_ABI/.libs -lsqlite3 \
-L${NDK_LIB_DIR} -lc++abi ${NDK_LIB_UNWIND}"
if [ $ON_WINDOWS -eq 1 ]; then
OSCMD=.cmd
fi
###########
# LINKING #
###########
fi # ${BUILD_ML} = "1"
echo -e "ndk-build vlc"
$ANDROID_NDK/ndk-build$OSCMD -C libvlc \
APP_STL="c++_static" \
APP_CPPFLAGS="-frtti -fexceptions" \
VLC_SRC_DIR="$VLC_SRC_DIR" \
VLC_BUILD_DIR="$VLC_SRC_DIR/$VLC_BUILD_DIR" \
VLC_CONTRIB="$VLC_CONTRIB" \
VLC_CONTRIB_LDFLAGS="$VLC_CONTRIB_LDFLAGS" \
VLC_MODULES="$VLC_MODULES" \
VLC_LDFLAGS="$VLC_LDFLAGS -latomic" \
MEDIALIBRARY_LDLIBS="${MEDIALIBRARY_LDLIBS}" \
MEDIALIBRARY_INCLUDE_DIR=${MEDIALIBRARY_BUILD_DIR}/include \
$ANDROID_NDK/ndk-build$OSCMD -C medialibrary \
APP_STL="c++_shared" \
LOCAL_CPP_FEATURES="rtti exceptions" \
APP_BUILD_SCRIPT=jni/Android.mk \
APP_PLATFORM=android-${ANDROID_API} \
APP_ABI=${ANDROID_ABI} \
NDK_PROJECT_PATH=jni \
NDK_TOOLCHAIN_VERSION=clang \
NDK_DEBUG=${NDK_DEBUG} \
BUILD_ML=${BUILD_ML}
checkfail "ndk-build failed"
$ANDROID_NDK/ndk-build$OSCMD -C libvlc \
APP_BUILD_SCRIPT=jni/loader/Android.mk \
APP_PLATFORM=android-${ANDROID_API} \
APP_ABI=${ANDROID_ABI} \
NDK_PROJECT_PATH=jni/loader \
NDK_TOOLCHAIN_VERSION=clang
MEDIALIBRARY_LDLIBS="${MEDIALIBRARY_LDLIBS}" \
MEDIALIBRARY_INCLUDE_DIR=${MEDIALIBRARY_BUILD_DIR}/include \
NDK_DEBUG=${NDK_DEBUG}
checkfail "ndk-build failed for libvlc"
fi # ${BUILD_ML} = "1"
echo "Dumping dbg symbols info ${OUT_DBG_DIR}"
......@@ -862,3 +860,4 @@ OUT_DBG_DIR=.dbg/${ANDROID_ABI}
mkdir -p $OUT_DBG_DIR
cp -a libvlc/jni/obj/local/${ANDROID_ABI}/*.so ${OUT_DBG_DIR}
cp -a medialibrary/jni/obj/local/${ANDROID_ABI}/*.so ${OUT_DBG_DIR}
......@@ -230,7 +230,7 @@ fi
# Fetch VLC source #
####################
TESTED_HASH=01083e3
TESTED_HASH=b7573bd
if [ ! -d "vlc" ]; then
diagnostic "VLC source not found, cloning"
git clone https://git.videolan.org/git/vlc/vlc-3.0.git vlc
......
......@@ -37,6 +37,8 @@ mkdir -p "$TMP_PATH"/jni
cp -r "$SCRIPT_PATH"/libvlc/jni/libs "$TMP_PATH"
cp -r "$SCRIPT_PATH"/libvlc/jni/obj "$TMP_PATH"
cp -r "$SCRIPT_PATH"/medialibrary/jni/libs "$TMP_PATH"
cp -r "$SCRIPT_PATH"/medialibrary/jni/obj "$TMP_PATH"
ln -s "$TMP_PATH"/obj "$TMP_PATH"/jni
......
......@@ -6,7 +6,7 @@ android {
sourceSets {
main {
jni.srcDirs = [] // Prevent gradle from building native code with ndk; we have our own Makefile for it.
jniLibs.srcDirs = [ 'jni/libs', 'jni/loader/libs', 'private_libs/libs' ] // Where generated .so files are placed.
jniLibs.srcDirs = [ 'jni/libs' ] // Where generated .so files are placed.
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
......
LOCAL_PATH := $(call my-dir)
MEDIALIBRARY_JNI_DIR := $(LOCAL_PATH)/../../medialibrary/jni
# libvlc jni static library
# libvlcjni
include $(CLEAR_VARS)
LOCAL_MODULE := vlcjni_static
LOCAL_MODULE := libvlcjni
LOCAL_SRC_FILES := libvlcjni.c
LOCAL_SRC_FILES += libvlcjni-mediaplayer.c
LOCAL_SRC_FILES += libvlcjni-vlcobject.c
LOCAL_SRC_FILES += libvlcjni-media.c libvlcjni-medialist.c libvlcjni-mediadiscoverer.c libvlcjni-rendererdiscoverer.c
LOCAL_SRC_FILES += libvlcjni-dialog.c
LOCAL_SRC_FILES += libvlcjni-glrenderer.c
LOCAL_SRC_FILES += thumbnailer.c
LOCAL_SRC_FILES += std_logger.c
LOCAL_SRC_FILES += dummy.cpp
LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/include $(VLC_BUILD_DIR)/include $(MEDIALIBRARY_JNI_DIR) $(LOCAL_PATH)/loader
LOCAL_C_INCLUDES := $(VLC_SRC_DIR)/include $(VLC_BUILD_DIR)/include $(MEDIALIBRARY_JNI_DIR)
LOCAL_CFLAGS := -std=c11
LOCAL_CXXFLAGS := -std=c++11
include $(BUILD_STATIC_LIBRARY)
LOCAL_LDLIBS := -llog
LOCAL_LDLIBS += -lEGL -lGLESv2 #libvlcjni-glrenderer.c
LOCAL_SHARED_LIBRARIES := libvlc
include $(BUILD_SHARED_LIBRARY)
# libvlc dynamic library
# libvlc
include $(CLEAR_VARS)
LOCAL_MODULE := vlcjni
LOCAL_SRC_FILES := libvlcjni-modules.c libvlcjni-symbols.c
LOCAL_MODULE := libvlc
LOCAL_SRC_FILES := libvlcjni-modules.c libvlcjni-symbols.c dummy.cpp
LOCAL_LDFLAGS := -L$(VLC_CONTRIB)/lib
LOCAL_LDLIBS := \
$(VLC_MODULES) \
......@@ -35,18 +35,7 @@ LOCAL_LDLIBS := \
-lavcodec -lebml \
-llua \
-lgcrypt -lgpg-error \
$(MEDIALIBRARY_LDLIBS) \
$(VLC_LDFLAGS) \
-llog
LOCAL_WHOLE_STATIC_LIBRARIES := libvlcjni_static
ifeq ($(BUILD_ML), 1)
LOCAL_WHOLE_STATIC_LIBRARIES += libmla
endif
LOCAL_CXXFLAGS := -std=c++11
include $(BUILD_SHARED_LIBRARY)
ifeq ($(BUILD_ML), 1)
JNILOADER_INCLUDES := $(LOCAL_PATH)/loader
$(call import-add-path, $(MEDIALIBRARY_JNI_DIR))
$(call import-module, .)
endif
/* Force ndk build system to link with c++_shared and install it */
/*****************************************************************************
* libvlcjni-glrenderer.c
*****************************************************************************
* Copyright © 2018 VLC authors, VideoLAN and VideoLabs
*
* 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.
*****************************************************************************/
#include <assert.h>
#include <pthread.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include "libvlcjni-vlcobject.h"
#define THREAD_NAME "GLRenderer"
extern JNIEnv *jni_get_env(const char *name);
struct glrenderer
{
pthread_mutex_t lock;
EGLint egl_version;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
unsigned width;
unsigned height;
GLuint texs[3];
GLuint fbos[3];
size_t idx_render;
size_t idx_swap;
size_t idx_display;
bool updated;
bool destroy_unused_egl;
};
static struct glrenderer *
GLRenderer_getInstance(JNIEnv *env, jobject thiz)
{
struct glrenderer *glr = (struct glrenderer*)(intptr_t)
(*env)->GetLongField(env, thiz, fields.GLRenderer.mInstanceID);
if (!glr)
throw_Exception(env, VLCJNI_EX_ILLEGAL_STATE,
"can't get GLRenderer instance");
return glr;
}
static void
GLRenderer_setInstance(JNIEnv *env, jobject thiz, struct glrenderer *glr)
{
(*env)->SetLongField(env, thiz, fields.GLRenderer.mInstanceID,
(jlong)(intptr_t)glr);
}
static inline void swap(size_t *a, size_t *b)
{
size_t tmp = *a;
*a = *b;
*b = tmp;
}
static void
glrenderer_destroy_egl(struct glrenderer *glr)
{
if (glr->display != EGL_NO_DISPLAY)
{
if (glr->context != EGL_NO_CONTEXT)
{
eglDestroyContext(glr->display, glr->context);
glr->context = EGL_NO_CONTEXT;
}
if (glr->surface != EGL_NO_SURFACE)
{
eglDestroySurface(glr->display, glr->surface);
glr->surface = EGL_NO_SURFACE;
}
glr->display = EGL_NO_DISPLAY;
}
glr->destroy_unused_egl = false;
}
static bool
gl_setup(void* opaque)
{
struct glrenderer *glr = opaque;
pthread_mutex_lock(&glr->lock);
glr->width = glr->height = 0;
glr->updated = false;
glr->destroy_unused_egl = false;
pthread_mutex_unlock(&glr->lock);
return true;
}
static void
gl_cleanup_locked(struct glrenderer *glr)
{
if (glr->width == 0 && glr->height == 0)
return;
glDeleteTextures(3, glr->texs);
glDeleteFramebuffers(3, glr->fbos);
glr->width = glr->height = 0;
}
static void
gl_resize_locked(struct glrenderer *glr, unsigned width, unsigned height)
{
if (width != glr->width || height != glr->height)
gl_cleanup_locked(glr);
glGenTextures(3, glr->texs);
glGenFramebuffers(3, glr->fbos);
for (int i = 0; i < 3; i++)
{
glBindTexture(GL_TEXTURE_2D, glr->texs[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, glr->fbos[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glr->texs[i], 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
return;
glr->width = width;
glr->height = height;
glBindFramebuffer(GL_FRAMEBUFFER, glr->fbos[glr->idx_render]);
}
static void
gl_cleanup(void* opaque)
{
struct glrenderer *glr = opaque;
pthread_mutex_lock(&glr->lock);
if (glr->display)
{
EGLBoolean ret = eglMakeCurrent(glr->display, glr->surface,
glr->surface, glr->context);
if (ret)
{
gl_cleanup_locked(glr);
eglMakeCurrent(glr->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
}
if (glr->destroy_unused_egl)
glrenderer_destroy_egl(glr);
glr->width = glr->height = 0;
pthread_mutex_unlock(&glr->lock);
}
static void
gl_resize(void* opaque, unsigned width, unsigned height)
{
struct glrenderer *glr = opaque;
pthread_mutex_lock(&glr->lock);
gl_resize_locked(glr, width, height);
pthread_mutex_unlock(&glr->lock);
}
static void
gl_swap(void* opaque)
{
struct glrenderer *glr = opaque;
pthread_mutex_lock(&glr->lock);
glr->updated = true;
swap(&glr->idx_swap, &glr->idx_render);
glBindFramebuffer(GL_FRAMEBUFFER, glr->fbos[glr->idx_render]);
pthread_mutex_unlock(&glr->lock);
}
static bool
gl_makeCurrent(void* opaque, bool enter)
{
struct glrenderer *glr = opaque;
pthread_mutex_lock(&glr->lock);
EGLBoolean ret;
if (!glr->display)
ret = EGL_FALSE;
else if (enter)
ret = eglMakeCurrent(glr->display, glr->surface, glr->surface, glr->context);
else
ret = eglMakeCurrent(glr->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
pthread_mutex_unlock(&glr->lock);
return ret;
}
static void*
gl_getProcAddress(void* opaque, const char* fct_name)
{
(void) opaque;
return eglGetProcAddress(fct_name);
}
void
Java_org_videolan_libvlc_GLRenderer_nativeInit(JNIEnv *env, jobject thiz,
jobject thizmp,
jint egl_version)
{
if (egl_version != 2 && egl_version != 3)
{
throw_Exception(env, VLCJNI_EX_ILLEGAL_ARGUMENT,
"Invalid EGLVersion (should be 2 or 3)");
}
vlcjni_object *mpobj = VLCJniObject_getInstance(env, thizmp);
if (!mpobj)
return;
if (!mpobj->u.p_mp)
{
throw_Exception(env, VLCJNI_EX_ILLEGAL_STATE,
"MediaPlayer instance invalid");
return;
}
struct glrenderer *glr = malloc(sizeof(*glr));
if (!glr)
{
throw_Exception(env, VLCJNI_EX_OUT_OF_MEMORY, "GLRenderer");
return;
}
glr->egl_version = egl_version;
glr->display = EGL_NO_DISPLAY;
glr->surface = EGL_NO_SURFACE;
glr->context = EGL_NO_CONTEXT;
glr->width = glr->height = 0;
glr->idx_render = 0;
glr->idx_swap = 1;
glr->idx_display = 2;
glr->updated = false;
pthread_mutex_init(&glr->lock, NULL);
libvlc_video_set_opengl_callbacks(mpobj->u.p_mp,
libvlc_gl_engine_gles2,
gl_setup,
gl_cleanup,
gl_resize,
gl_swap,
gl_makeCurrent,
gl_getProcAddress,
glr);
GLRenderer_setInstance(env, thiz, glr);
}
void
Java_org_videolan_libvlc_GLRenderer_nativeRelease(JNIEnv *env, jobject thiz)
{
struct glrenderer *glr = GLRenderer_getInstance(env, thiz);
if (!glr)
return;
pthread_mutex_destroy(&glr->lock);
glrenderer_destroy_egl(glr);
free(glr);
GLRenderer_setInstance(env, thiz, NULL);
}
void
Java_org_videolan_libvlc_GLRenderer_nativeOnSurfaceCreated(JNIEnv *env, jobject thiz)
{
struct glrenderer *glr = GLRenderer_getInstance(env, thiz);
if (!glr)
return;
pthread_mutex_lock(&glr->lock);
glrenderer_destroy_egl(glr);
const EGLint config_attr[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
const EGLint surface_attr[] = {
EGL_WIDTH, 2,
EGL_HEIGHT, 2,
EGL_NONE
};
const EGLint ctx_attr[] = {
EGL_CONTEXT_CLIENT_VERSION, glr->egl_version,
EGL_NONE
};
EGLConfig config;
EGLint num_configs;
glr->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (glr->display == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS)
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglGetCurrentDisplay error: %x", eglGetError());
goto error;
}
if (!eglInitialize(glr->display, NULL, NULL))
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglInitialize() error: %x", eglGetError());
goto error;
}
if (!eglChooseConfig(glr->display, config_attr, &config, 1, &num_configs)
|| eglGetError() != EGL_SUCCESS)
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglGetConfigAttrib() error: %x", eglGetError());
goto error;
}
EGLContext current_ctx = eglGetCurrentContext();
if (eglGetError() != EGL_SUCCESS)
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglGetCurrentContext() error %x", eglGetError());
goto error;
}
glr->surface = eglCreatePbufferSurface(glr->display, config, surface_attr);
if (glr->surface == EGL_NO_SURFACE || eglGetError() != EGL_SUCCESS)
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglCreatePbufferSurface() error %x", eglGetError());
goto error;
}
glr->context = eglCreateContext(glr->display, config, current_ctx, ctx_attr);
if (glr->context == EGL_NO_CONTEXT || eglGetError() != EGL_SUCCESS)
{
throw_Exception(env, VLCJNI_EX_RUNTIME,
"eglCreateContext() error: %x", eglGetError());
goto error;
}
pthread_mutex_unlock(&glr->lock);
return;
error:
glrenderer_destroy_egl(glr);
pthread_mutex_unlock(&glr->lock);
}
void
Java_org_videolan_libvlc_GLRenderer_nativeOnSurfaceDestroyed(JNIEnv *env, jobject thiz)
{
struct glrenderer *glr = GLRenderer_getInstance(env, thiz);
if (!glr)
return;
pthread_mutex_lock(&glr->lock);
if (glr->width == 0 && glr->height == 0)
glrenderer_destroy_egl(glr);
else
glr->destroy_unused_egl = true;
pthread_mutex_unlock(&glr->lock);
}
jint
Java_org_videolan_libvlc_GLRenderer_nativeGetVideoTexture(JNIEnv *env, jobject thiz,
jobject point)
{
struct glrenderer *glr = GLRenderer_getInstance(env, thiz);
if (!glr)
return 0;
pthread_mutex_lock(&glr->lock);
jint tex_id;
if (glr->width == 0 || glr->height == 0)
tex_id = 0;
else
{
if (glr->updated)
{
swap(&glr->idx_swap, &glr->idx_display);
glr->updated = false;
}
tex_id = glr->texs[glr->idx_display];
if (point != NULL)
(*env)->CallVoidMethod(env, point, fields.Point.setID,
glr->width, glr->height);
}
pthread_mutex_unlock(&glr->lock);
return tex_id;
}
......@@ -22,6 +22,7 @@
#define LIBVLCJNI_VLCOBJECT_H
#include <stdbool.h>
#include <stdlib.h>
#include <jni.h>
#include <vlc/vlc.h>
......
......@@ -30,7 +30,7 @@
#include <vlc/vlc.h>
#include "jniloader.h"
#include <jni.h>
#include <android/api-level.h>
......@@ -40,6 +40,8 @@
struct fields fields;
#define VLC_JNI_VERSION JNI_VERSION_1_2
#define THREAD_NAME "libvlcjni"
JNIEnv *jni_get_env(const char *name);
......@@ -97,10 +99,15 @@ JNIEnv *jni_get_env(const char *name)
static std_logger *p_std_logger = NULL;
#endif
int VLCJNI_OnLoad(JavaVM *vm, JNIEnv* env)
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
// Keep a reference on the Java VM.
myVm = vm;
if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
return -1;
/* Create a TSD area and setup a destroy callback when a thread that
* previously set the jni_env_key is canceled or exited */
if (pthread_key_create(&jni_env_key, jni_detach_thread) != 0)
......@@ -185,6 +192,8 @@ int VLCJNI_OnLoad(JavaVM *vm, JNIEnv* env)
"org/videolan/libvlc/RendererDiscoverer$Description", true);
GET_CLASS(fields.Dialog.clazz,
"org/videolan/libvlc/Dialog", true);
GET_CLASS(fields.GLRenderer.clazz,
"org/videolan/libvlc/GLRenderer", true);
GET_ID(GetFieldID,
fields.VLCObject.mInstanceID,
......@@ -196,6 +205,11 @@ int VLCJNI_OnLoad(JavaVM *vm, JNIEnv* env)
fields.MediaPlayer.Equalizer.clazz,
"mInstance", "J");
GET_ID(GetFieldID,
fields.GLRenderer.mInstanceID,
fields.GLRenderer.clazz,
"mInstance", "J");
GET_ID(GetMethodID,
fields.VLCObject.dispatchEventFromNativeID,
fields.VLCObject.clazz,
......@@ -339,14 +353,24 @@ int VLCJNI_OnLoad(JavaVM *vm, JNIEnv* env)
"updateProgressFromNative",
"(Lorg/videolan/libvlc/Dialog;FLjava/lang/String;)V");
jclass pointCLazz;
GET_CLASS(pointCLazz, "android/graphics/Point", false);
GET_ID(GetMethodID, fields.Point.setID, pointCLazz, "set", "(II)V");
#undef GET_CLASS
#undef GET_ID
return 0;
LOGD("JNI interface loaded.");
return VLC_JNI_VERSION;
}
void VLCJNI_OnUnload(JavaVM *vm, JNIEnv *env)
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
return;
(*env)->DeleteGlobalRef(env, fields.IllegalStateException.clazz);
(*env)->DeleteGlobalRef(env, fields.IllegalArgumentException.clazz);
(*env)->DeleteGlobalRef(env, fields.RuntimeException.clazz);
......
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jniloader
LOCAL_SRC_FILES := jniloader.c
LOCAL_LDFLAGS := -ldl -llog
include $(BUILD_SHARED_LIBRARY)
/*****************************************************************************
* jniloader.c
*****************************************************************************
* Copyright © 2017 VLC authors and VideoLAN
*
* 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.
*****************************************************************************/
#include <dlfcn.h>
#include <stdbool.h>
#include <android/log.h>
#include "jniloader.h"
#define LOG_TAG "VLC"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
static void *handle;
static bool has_ml;
/* jniloader.so is a shared C library used to receive JNI_OnLoad callback.
* Indeed, as JNI_OnLoad is already implemented in VLC core for its internal
* configuration, we can't have an other JNI_OnLoad implementation inside the
* libvlcjni.so.
*/
int JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
{
LOGE("GetEnv failed");
return -1;
}
handle = dlopen("libvlcjni.so", RTLD_LAZY);
if (!handle)
{
LOGE("could not link libvlcjni.so");
return -1;
}
int (*load)(JavaVM *, JNIEnv*);
load = dlsym(handle, "VLCJNI_OnLoad");
if (!load || load(vm, env) != 0)
{
if (!load)
LOGE("could not find VLCJNI_OnLoad");
else
LOGE("VLCJNI_OnLoad failed");
return -1;
}
/* MediaLibraryJNI_OnLoad is not mandatory */
load = dlsym(handle, "MediaLibraryJNI_OnLoad");
if (load && load(vm, env) != 0)
{
LOGE("MediaLibraryJNI_OnLoad failed");
return -1;
}
has_ml = !!load;
return VLC_JNI_VERSION;
}
void JNI_OnUnload(JavaVM *vm, void *reserved)
{
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
return;
void (*unload)(JavaVM *, JNIEnv*);
if (has_ml)
{
unload = dlsym(handle, "MediaLibraryJNI_OnUnload");
if (unload)
unload(vm, env);
else
LOGE("could not find MediaLibraryJNI_OnUnload");
}
unload = dlsym(handle, "VLCJNI_OnUnload");
if (unload)
unload(vm, env);
else
LOGE("could not find VLCJNI_OnUnload");
dlclose(handle);
}
/*****************************************************************************
* jniloader.h
*****************************************************************************
* Copyright © 2017 VLC authors and VideoLAN
*
* 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.
*****************************************************************************/
#include <jni.h>
#define VLC_JNI_VERSION JNI_VERSION_1_2
#ifdef __cplusplus
extern "C" {
#endif
int MediaLibraryJNI_OnLoad(JavaVM *vm, JNIEnv *env);
void MediaLibraryJNI_OnUnload(JavaVM *vm, JNIEnv *env);
int VLCJNI_OnLoad(JavaVM *vm, JNIEnv *env);
void VLCJNI_OnUnload(JavaVM *vm, JNIEnv *env);
#ifdef __cplusplus
}
#endif
......@@ -114,6 +114,13 @@ struct fields {
jmethodID cancelFromNativeID;
jmethodID updateProgressFromNativeID;
} Dialog;
struct {
jmethodID setID;
} Point;
struct {
jclass clazz;
jfieldID mInstanceID;
} GLRenderer;
};
extern struct fields fields;
......
......@@ -189,6 +189,7 @@ public class AWindow implements IVLCVout {
private OnNewVideoLayoutListener mOnNewVideoLayoutListener = null;
private ArrayList<IVLCVout.Callback> mIVLCVoutCallbacks = new ArrayList<IVLCVout.Callback>();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private boolean mUseGLRenderer = false;
/* synchronized Surfaces accessed by an other thread from JNI */
private final Surface[] mSurfaces;
private long mCallbackNativeHandle = 0;
......@@ -215,6 +216,8 @@ public class AWindow implements IVLCVout {
}
private void ensureInitState() throws IllegalStateException {
if (mUseGLRenderer)
throw new IllegalStateException("Can't set view when using GL Renderer.");
if (mSurfacesState.get() != SURFACE_STATE_INIT)
throw new IllegalStateException("Can't set view when already attached. " +
"Current state: " + mSurfacesState.get() + ", " +
......@@ -351,6 +354,11 @@ public class AWindow implements IVLCVout {
mSurfaceTextureThread.release();
}
@MainThread
protected void useGLRenderer() {
mUseGLRenderer = true;
}
@Override
@MainThread
public boolean areViewsAttached() {
......
/*****************************************************************************
* GLRenderer.java
*****************************************************************************
* Copyright © 2018 VLC authors and VideoLAN
**
* 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.
*****************************************************************************/
package org.videolan.libvlc;
import android.graphics.Point;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class GLRenderer {
private MediaPlayer.SurfaceListener mListener;
private int mLastTextureId = 0;
private boolean mFrameUpdated = false;
private boolean mSurfaceCreated = false;
GLRenderer(int eglContextClientVersion, MediaPlayer mp, MediaPlayer.SurfaceListener listener) {
if (listener == null)
throw new IllegalArgumentException("listener can't be null");
mListener = listener;
nativeInit(mp, eglContextClientVersion);
}
void release() {
nativeRelease();
}
@SuppressWarnings("unused") /* Used from JNI */
private long mInstance = 0;
synchronized boolean isValid() {
return mSurfaceCreated;
}
/**
* This method need to be called when a new EGL Context is created (from the same thread).
*
* Example: call this method from
* {@link android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}.
*/
synchronized public void onSurfaceCreated() {
nativeOnSurfaceCreated();
if (!mSurfaceCreated) {
mSurfaceCreated = true;
mListener.onSurfaceCreated();
}
}
/**
* This method need to be called when the EGL Context is destroyed (from any thread).
*
* Example: call this method from {@link android.opengl.GLSurfaceView.Renderer#onPause()}.
*/
synchronized public void onSurfaceDestroyed() {
if (mSurfaceCreated) {
mSurfaceCreated = false;
mListener.onSurfaceDestroyed();
nativeOnSurfaceDestroyed();
}
}
/**
* Get the current GL texture ID.
*
* @param videoSize Used to get the size of the texture (can be null)
* @return a valid GL_TEXTURE_2D id or 0 if no video is playing.
*/
public int getVideoTexture(Point videoSize) {
int newTextureId = nativeGetVideoTexture(videoSize);
if (newTextureId != mLastTextureId) {
if (newTextureId != 0)
mFrameUpdated = true;
}
else {
mLastTextureId = newTextureId;
mFrameUpdated = false;
}
return newTextureId;
}
/**
* Check if the video frame changed
*
* This method need to be called after {@link GLRenderer#getVideoTexture}
*
* @return true of the video frame changed
*/
public boolean isVideoFrameUpdated() {
return mFrameUpdated;
}
private native void nativeInit(MediaPlayer mp, int eglContextClientVersion);
private native void nativeRelease();
private native void nativeOnSurfaceCreated();
private native void nativeOnSurfaceDestroyed();
private native int nativeGetVideoTexture(Point point);
}
\ No newline at end of file
......@@ -136,8 +136,9 @@ public class LibVLC extends VLCObject<LibVLC.Event> {
sLoaded = true;
try {
System.loadLibrary("c++_shared");
System.loadLibrary("vlc");
System.loadLibrary("vlcjni");
System.loadLibrary("jniloader");
} catch (UnsatisfiedLinkError ule) {
Log.e(TAG, "Can't load vlcjni library: " + ule);
/// FIXME Alert user
......
......@@ -411,6 +411,8 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
private final AWindow mWindow = new AWindow(mSurfaceListener);
private GLRenderer mGLRenderer = null;
private synchronized void updateAudioOutputDevice(long encodingFlags, String defaultDevice) {
mCanDoPassthrough = encodingFlags != 0;
final String newDeviceId = mAudioDigitalOutputEnabled && mCanDoPassthrough ? "encoded:" + encodingFlags : defaultDevice;
......@@ -573,6 +575,16 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
return mWindow;
}
public GLRenderer enableGLRenderer(int eglContextClientVersion) {
if (mWindow.areViewsAttached())
throw new IllegalArgumentException("can't work with views attached to IVLCVout");
if (mGLRenderer != null)
throw new IllegalArgumentException("GLRenderer already enabled");
mWindow.useGLRenderer();
mGLRenderer = new GLRenderer(eglContextClientVersion, this, mSurfaceListener);
return mGLRenderer;
}
/**
* Set a Media
*
......@@ -630,7 +642,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
if (mListenAudioPlug)
registerAudioPlug(true);
mPlayRequested = true;
if (mWindow.areSurfacesWaiting())
if (mWindow.areSurfacesWaiting() || (mGLRenderer != null && !mGLRenderer.isValid()))
return;
}
mPlaying = true;
......@@ -888,7 +900,8 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
*/
public boolean setVideoTrack(int index) {
/* Don't activate a video track is surfaces are not ready */
if (index == -1 || (mWindow.areViewsAttached() && !mWindow.areSurfacesWaiting())) {
if (index == -1 || (mWindow.areViewsAttached() && !mWindow.areSurfacesWaiting())
|| (mGLRenderer != null && mGLRenderer.isValid())) {
return nativeSetVideoTrack(index);
} else
return false;
......@@ -1204,6 +1217,8 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
@Override
protected void onReleaseNative() {
if (mGLRenderer != null)
mGLRenderer.release();
mWindow.detachViews();
registerAudioPlug(false);
......
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := medialibrary.cpp AndroidMediaLibrary.cpp AndroidDeviceLister.cpp utils.cpp
LOCAL_MODULE := mla
LOCAL_C_INCLUDES := $(MEDIALIBRARY_INCLUDE_DIR) $(JNILOADER_INCLUDES)
LOCAL_CFLAGS := $(MEDIALIBRARY_CFLAGS)
include $(BUILD_STATIC_LIBRARY)
LOCAL_MODULE_FILENAME := libmla
LOCAL_LDLIBS := $(MEDIALIBRARY_LDLIBS) -llog
LOCAL_C_INCLUDES := $(MEDIALIBRARY_INCLUDE_DIR)
include $(BUILD_SHARED_LIBRARY)
#include "AndroidMediaLibrary.h"
#define LOG_TAG "VLC/JNI/AndroidMediaLibrary"
#include "log.h"
#include "jniloader.h"
#define THREAD_NAME "AndroidMedialibrary"
#define FLAG_MEDIA_UPDATED_AUDIO 1 << 0
......
......@@ -3,7 +3,7 @@
#include <algorithm>
#include <string>
#include "jniloader.h"
#include <jni.h>
#include <medialibrary/IDeviceLister.h>
#define LOG_TAG "VLC/JNI/MediaLibrary"
#include "log.h"
......@@ -1733,11 +1733,14 @@ static void jni_detach_thread(void *data)
myVm->DetachCurrentThread();
}
extern "C" {
int MediaLibraryJNI_OnLoad(JavaVM *vm, JNIEnv* env)
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
myVm = vm;
if (vm->GetEnv((void**) &env, VLC_JNI_VERSION) != JNI_OK)
return -1;
#define GET_CLASS(clazz, str, b_globlal) do { \
(clazz) = env->FindClass((str)); \
if (!(clazz)) { \
......@@ -1761,7 +1764,6 @@ int MediaLibraryJNI_OnLoad(JavaVM *vm, JNIEnv* env)
} \
} while (0)
jclass Version_clazz;
jfieldID SDK_INT_fieldID;
GET_CLASS(Version_clazz, "android/os/Build$VERSION", false);
......@@ -1960,17 +1962,22 @@ int MediaLibraryJNI_OnLoad(JavaVM *vm, JNIEnv* env)
#undef GET_CLASS
#undef GET_ID
return 0;
return VLC_JNI_VERSION;
}
void MediaLibraryJNI_OnUnload(JavaVM *vm, JNIEnv* env)
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, VLC_JNI_VERSION) != JNI_OK)
return;
env->DeleteGlobalRef(ml_fields.IllegalArgumentException.clazz);
env->DeleteGlobalRef(ml_fields.IllegalStateException.clazz);
env->DeleteGlobalRef(ml_fields.MediaLibrary.clazz);
env->DeleteGlobalRef(ml_fields.MediaWrapper.clazz);
}
}
static inline void throw_IllegalStateException(JNIEnv *env, const char *p_error)
{
......
......@@ -24,6 +24,8 @@
#include <medialibrary/Types.h>
#include <medialibrary/IMediaLibrary.h>
#define VLC_JNI_VERSION JNI_VERSION_1_2
struct fields {
jint SDK_INT;
struct IllegalStateException {
......
......@@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
......@@ -100,6 +101,14 @@ public class Medialibrary {
|| dbDirectory == null || !dbDirectory.canWrite())
return ML_INIT_FAILED;
LibVLC.loadLibraries();
try {
System.loadLibrary("c++_shared");
System.loadLibrary("mla");
} catch (UnsatisfiedLinkError ule)
{
Log.e(TAG, "Can't load mla: " + ule);
return ML_INIT_FAILED;
}
int initCode = nativeInit(dbDirectory+ VLC_MEDIA_DB_NAME, extFilesDir+ THUMBS_FOLDER_NAME);
mIsInitiated = initCode != ML_INIT_FAILED;
return initCode;
......
......@@ -5,6 +5,11 @@ apply plugin: 'kotlin-kapt'
android {
packagingOptions {
exclude 'META-INF/main.kotlin_module'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/armeabi/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
}
dexOptions {
......