Commit 918ed4d1 authored by Thomas Guillem's avatar Thomas Guillem

android: merge android_surface with android_window

This commits remove the android_surface module. The old android surface API is
now used by the android_window module via native_window_api_t.
parent 7d72f2c9
......@@ -3251,16 +3251,6 @@ dnl
PKG_ENABLE_MODULES_VLC([SVG], [], [librsvg-2.0 >= 2.9.0], [SVG rendering library],[auto])
PKG_ENABLE_MODULES_VLC([SVGDEC], [], [librsvg-2.0 >= 2.9.0 cairo >= 1.13.1], [SVG image decoder library],[auto])
dnl
dnl android surface module
dnl
AC_ARG_ENABLE(android-surface,
[ --enable-android-surface Android Surface video output module (default disabled)])
if test "${enable_android_surface}" = "yes"; then
VLC_ADD_PLUGIN([android_surface])
VLC_ADD_PLUGIN([android_window])
fi
dnl
dnl Windows DirectX module
dnl
......
......@@ -36,7 +36,6 @@ $Id$
* android_audiotrack: audio output for Android, based on AudioTrack
* android_logger: logger output to native Android logs (adb logcat)
* android_native_window: Android native window provider module
* android_surface: video output for Android, based on Surface
* android_window: Android direct/undirect rendering video output
* antiflicker: anti-flicker video filter
* araw: Pseudo audio decoder for raw PCM
......
......@@ -276,16 +276,9 @@ libandroid_window_plugin_la_LIBADD = $(LIBDL)
libandroid_window_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
EXTRA_LTLIBRARIES += libandroid_window_plugin.la
libandroid_surface_plugin_la_SOURCES = video_output/android/surface.c video_output/android/utils.c video_output/android/utils.h
libandroid_surface_plugin_la_CFLAGS = $(AM_CFLAGS)
libandroid_surface_plugin_la_LIBADD = $(LIBDL)
libandroid_surface_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
EXTRA_LTLIBRARIES += libandroid_surface_plugin.la
if HAVE_ANDROID
vout_LTLIBRARIES += libandroid_native_window_plugin.la
vout_LTLIBRARIES += $(LTLIBandroid_window)
vout_LTLIBRARIES += $(LTLIBandroid_surface)
if HAVE_EGL
vout_LTLIBRARIES += libegl_android_plugin.la
endif
......
......@@ -75,6 +75,7 @@ extern void jni_UnlockAndroidSurface();
extern void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
extern int jni_GetWindowSize(int *width, int *height);
extern void jni_getMouseCoordinates(int *, int *, int *, int *);
static const vlc_fourcc_t subpicture_chromas[] =
{
......@@ -85,6 +86,7 @@ static const vlc_fourcc_t subpicture_chromas[] =
static picture_pool_t *Pool (vout_display_t *, unsigned);
static void Prepare(vout_display_t *, picture_t *, subpicture_t *);
static void Display(vout_display_t *, picture_t *, subpicture_t *);
static void Manage(vout_display_t *vd);
static int Control(vout_display_t *, int, va_list);
typedef struct android_window android_window;
......@@ -137,6 +139,25 @@ struct vout_display_sys_t
uint8_t hash[16];
};
#define PRIV_WINDOW_FORMAT_YV12 0x32315659
static inline int ChromaToAndroidHal(vlc_fourcc_t i_chroma)
{
switch (i_chroma) {
case VLC_CODEC_YV12:
case VLC_CODEC_I420:
return PRIV_WINDOW_FORMAT_YV12;
case VLC_CODEC_RGB16:
return WINDOW_FORMAT_RGB_565;
case VLC_CODEC_RGB32:
return WINDOW_FORMAT_RGBX_8888;
case VLC_CODEC_RGBA:
return WINDOW_FORMAT_RGBA_8888;
default:
return -1;
}
}
static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
{
unsigned int i_width, i_height;
......@@ -479,7 +500,7 @@ static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
p_window->i_pic_count = 1;
p_window->i_min_undequeued = 0;
if (!b_java_configured)
if (!b_java_configured && sys->anw.setBuffersGeometry)
return sys->anw.setBuffersGeometry(p_window->p_handle,
p_window->fmt.i_width,
p_window->fmt.i_height,
......@@ -631,9 +652,10 @@ static int Open(vlc_object_t *p_this)
return VLC_ENOMEM;
sys->p_library = LoadNativeWindowAPI(&sys->anw);
if (!sys->p_library) {
msg_Err(vd, "Could not initialize NativeWindow API.");
goto error;
if (!sys->p_library)
{
msg_Warn(vd, "Using old Android Surface");
LoadNativeSurfaceAPI(&sys->anw);
}
#ifdef USE_ANWP
......@@ -1103,3 +1125,24 @@ static int Control(vout_display_t *vd, int query, va_list args)
return VLC_EGENERIC;
}
}
static void Manage(vout_display_t *vd)
{
int x, y, button, action;
jni_getMouseCoordinates(&action, &button, &x, &y);
if (x >= 0 && y >= 0)
{
switch( action )
{
case AMOTION_EVENT_ACTION_DOWN:
vout_display_SendEventMouseMoved(vd, x, y);
vout_display_SendEventMousePressed(vd, button); break;
case AMOTION_EVENT_ACTION_UP:
vout_display_SendEventMouseMoved(vd, x, y);
vout_display_SendEventMouseReleased(vd, button); break;
case AMOTION_EVENT_ACTION_MOVE:
vout_display_SendEventMouseMoved(vd, x, y); break;
}
}
}
/*****************************************************************************
* androidsurface.c: android video output using Surface Flinger
*****************************************************************************
* Copyright © 2011 VideoLAN
*
* Authors: Ming Hu <tewilove@gmail.com>
* Ludovic Fauvet <etix@l0cal.com>
* Sébastien Toque <xilasz@gmail.com>
*
* 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 <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>
#include <dlfcn.h>
#include <jni.h>
#include "utils.h"
#ifndef ANDROID_SYM_S_LOCK
# define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb"
#endif
#ifndef ANDROID_SYM_S_LOCK2
# define ANDROID_SYM_S_LOCK2 "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"
#endif
#ifndef ANDROID_SYM_S_UNLOCK
# define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define CHROMA_TEXT N_("Chroma used")
#define CHROMA_LONGTEXT N_(\
"Force use of a specific chroma for output. Default is RGB32.")
#define CFG_PREFIX "androidsurface-"
static int Open (vlc_object_t *);
static void Close(vlc_object_t *);
vlc_module_begin()
set_category(CAT_VIDEO)
set_subcategory(SUBCAT_VIDEO_VOUT)
set_shortname("AndroidSurface")
set_description(N_("Android Surface video output"))
set_capability("vout display", 155)
add_shortcut("androidsurface", "android")
add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
set_callbacks(Open, Close)
vlc_module_end()
/*****************************************************************************
* JNI prototypes
*****************************************************************************/
extern jobject jni_LockAndGetAndroidJavaSurface();
extern void jni_UnlockAndroidSurface();
extern void *jni_AndroidJavaSurfaceToNativeSurface(jobject surf);
extern void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
// _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb
typedef void (*Surface_lock)(void *, void *, int);
// _ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE
typedef void (*Surface_lock2)(void *, void *, void *);
// _ZN7android7Surface13unlockAndPostEv
typedef void (*Surface_unlockAndPost)(void *);
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static picture_pool_t *Pool (vout_display_t *, unsigned);
static void Display(vout_display_t *, picture_t *, subpicture_t *);
static int Control(vout_display_t *, int, va_list);
/* */
typedef struct _SurfaceInfo {
uint32_t w;
uint32_t h;
uint32_t s;
uint32_t usage;
uint32_t format;
uint32_t* bits;
uint32_t reserved[2];
} SurfaceInfo;
/* */
struct vout_display_sys_t {
picture_pool_t *pool;
void *p_library;
Surface_lock s_lock;
Surface_lock2 s_lock2;
Surface_unlockAndPost s_unlockAndPost;
jobject jsurf;
void *native_surface;
int i_android_hal;
unsigned int i_alloc_width;
unsigned int i_alloc_height;
video_format_t fmt;
};
struct picture_sys_t {
void *surf;
SurfaceInfo info;
vout_display_sys_t *sys;
};
static int AndroidLockSurface(picture_t *);
static void AndroidUnlockSurface(picture_t *);
static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys)
{
void *p_library = dlopen(psz_lib, RTLD_NOW);
if (!p_library)
return NULL;
sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK));
sys->s_lock2 = (Surface_lock2)(dlsym(p_library, ANDROID_SYM_S_LOCK2));
sys->s_unlockAndPost =
(Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK));
if ((sys->s_lock || sys->s_lock2) && sys->s_unlockAndPost)
return p_library;
dlclose(p_library);
return NULL;
}
static void *InitLibrary(vout_display_sys_t *sys)
{
static const char *libs[] = {
"libsurfaceflinger_client.so",
"libgui.so",
"libui.so"
};
for (size_t i = 0; i < sizeof(libs) / sizeof(*libs); i++) {
void *lib = LoadSurface(libs[i], sys);
if (lib)
return lib;
}
return NULL;
}
static void UpdateLayout(vout_display_sys_t *sys)
{
unsigned int i_sar_num = 1, i_sar_den = 1;
unsigned int i_width, i_height;
if (sys->fmt.i_sar_num != 0 && sys->fmt.i_sar_den != 0) {
i_sar_num = sys->fmt.i_sar_num;
i_sar_den = sys->fmt.i_sar_den;
}
if (sys->i_alloc_width != 0 && sys->i_alloc_height != 0) {
i_width = sys->i_alloc_width;
i_height = sys->i_alloc_height;
} else {
i_width = sys->fmt.i_width;
i_height = sys->fmt.i_height;
}
jni_SetSurfaceLayout(i_width, i_height,
sys->fmt.i_visible_width,
sys->fmt.i_visible_height,
i_sar_num,
i_sar_den);
}
static int Open(vlc_object_t *p_this)
{
vout_display_t *vd = (vout_display_t *)p_this;
video_format_t fmt;
video_format_ApplyRotation(&fmt, &vd->fmt);
if (fmt.i_chroma == VLC_CODEC_ANDROID_OPAQUE)
return VLC_EGENERIC;
if (vout_display_IsWindowed(vd))
return VLC_EGENERIC;
/* Allocate structure */
vout_display_sys_t *sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
if (!sys)
goto error;
/* */
sys->p_library = InitLibrary(sys);
if (!sys->p_library) {
msg_Err(vd, "Could not initialize libandroid.so/libui.so/libgui.so/libsurfaceflinger_client.so!");
goto error;
}
/* Setup chroma */
char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
if( psz_fcc ) {
fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
free(psz_fcc);
} else
fmt.i_chroma = VLC_CODEC_RGB32;
switch(fmt.i_chroma) {
case VLC_CODEC_RGB16:
fmt.i_bmask = 0x0000001f;
fmt.i_gmask = 0x000007e0;
fmt.i_rmask = 0x0000f800;
break;
case VLC_CODEC_YV12:
case VLC_CODEC_I420:
fmt.i_chroma = VLC_CODEC_RGB32;
case VLC_CODEC_RGB32:
fmt.i_rmask = 0x000000ff;
fmt.i_gmask = 0x0000ff00;
fmt.i_bmask = 0x00ff0000;
break;
default:
return VLC_EGENERIC;
}
video_format_FixRgb(&fmt);
msg_Dbg(vd, "Pixel format %4.4s", (char*)&fmt.i_chroma);
sys->i_android_hal = ChromaToAndroidHal(fmt.i_chroma);
if (sys->i_android_hal == -1)
goto error;
sys->fmt = fmt;
UpdateLayout(sys);
/* Create the associated picture */
picture_sys_t *picsys = calloc(1, sizeof(picture_sys_t));
if (unlikely(picsys == NULL))
goto error;
picsys->sys = sys;
picture_resource_t resource = { .p_sys = picsys };
picture_t *picture = picture_NewFromResource(&fmt, &resource);
if (!picture) {
free(picsys);
goto error;
}
/* Wrap it into a picture pool */
picture_pool_configuration_t pool_cfg;
memset(&pool_cfg, 0, sizeof(pool_cfg));
pool_cfg.picture_count = 1;
pool_cfg.picture = &picture;
pool_cfg.lock = AndroidLockSurface;
pool_cfg.unlock = AndroidUnlockSurface;
sys->pool = picture_pool_NewExtended(&pool_cfg);
if (!sys->pool) {
picture_Release(picture);
goto error;
}
/* Setup vout_display */
vd->sys = sys;
vd->fmt = fmt;
vd->pool = Pool;
vd->display = Display;
vd->control = Control;
vd->prepare = NULL;
vd->manage = Manage;
/* Fix initial state */
vout_display_SendEventFullscreen(vd, false);
return VLC_SUCCESS;
error:
Close(p_this);
return VLC_ENOMEM;
}
static void Close(vlc_object_t *p_this)
{
vout_display_t *vd = (vout_display_t *)p_this;
vout_display_sys_t *sys = vd->sys;
if (sys) {
if (sys->pool)
picture_pool_Release(sys->pool);
if (sys->p_library)
dlclose(sys->p_library);
free(sys);
}
}
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
{
VLC_UNUSED(count);
return vd->sys->pool;
}
static int AndroidLockSurface(picture_t *picture)
{
picture_sys_t *picsys = picture->p_sys;
vout_display_sys_t *sys = picsys->sys;
SurfaceInfo *info = &picsys->info;
uint32_t sw, sh;
if (!sys->native_surface) {
picsys->surf = jni_LockAndGetAndroidJavaSurface();
if (unlikely(!picsys->surf)) {
jni_UnlockAndroidSurface();
return VLC_EGENERIC;
}
sys->native_surface = jni_AndroidJavaSurfaceToNativeSurface(picsys->surf);
jni_UnlockAndroidSurface();
if (!sys->native_surface)
return VLC_EGENERIC;
}
sw = sys->fmt.i_width;
sh = sys->fmt.i_height;
// For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
int align_pixels = (16 / picture->p[0].i_pixel_pitch) - 1;
uint32_t aligned_width = (sw + align_pixels) & ~align_pixels;
if (aligned_width != sys->i_alloc_width || sh != sys->i_alloc_height) {
bool configured;
if (jni_ConfigureSurface(picsys->surf,
aligned_width,
sh,
sys->i_android_hal,
&configured) == -1 || !configured) {
return VLC_EGENERIC;
}
sys->i_alloc_width = aligned_width;
sys->i_alloc_height = sh;
sys->native_surface = jni_AndroidJavaSurfaceToNativeSurface(picsys->surf);
UpdateLayout(sys);
}
if (sys->s_lock)
sys->s_lock(sys->native_surface, info, 1);
else
sys->s_lock2(sys->native_surface, info, NULL);
if (info->w != sys->i_alloc_width || info->h != sh) {
sys->s_unlockAndPost(sys->native_surface);
return VLC_EGENERIC;
}
picture->p[0].p_pixels = (uint8_t*)info->bits;
picture->p[0].i_lines = info->h;
picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * info->s;
return VLC_SUCCESS;
}
static void AndroidUnlockSurface(picture_t *picture)
{
picture_sys_t *picsys = picture->p_sys;
vout_display_sys_t *sys = picsys->sys;
if (sys->native_surface)
sys->s_unlockAndPost(sys->native_surface);
}
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
{
VLC_UNUSED(vd);
VLC_UNUSED(subpicture);
/* refcount lowers to 0, and pool_cfg.unlock is called */
picture_Release(picture);
}
static int Control(vout_display_t *vd, int query, va_list args)
{
VLC_UNUSED(args);
switch (query) {
case VOUT_DISPLAY_HIDE_MOUSE:
return VLC_SUCCESS;
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
{
vout_display_sys_t *sys = vd->sys;
const video_format_t *source = (const video_format_t *)va_arg(args, const video_format_t *);
if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
video_format_CopyCrop(&sys->fmt, source);
} else {
sys->fmt.i_sar_num = source->i_sar_num;
sys->fmt.i_sar_den = source->i_sar_den;
}
UpdateLayout(sys);
return VLC_SUCCESS;
}
default:
msg_Err(vd, "Unknown request in android vout display");
case VOUT_DISPLAY_CHANGE_FULLSCREEN:
case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
case VOUT_DISPLAY_CHANGE_ZOOM:
return VLC_EGENERIC;
}
}
......@@ -22,6 +22,151 @@
#include "utils.h"
#include <dlfcn.h>
/*
* Android Surface (pre android 2.3)
*/
extern void *jni_AndroidJavaSurfaceToNativeSurface(jobject surf);
#ifndef ANDROID_SYM_S_LOCK
# define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb"
#endif
#ifndef ANDROID_SYM_S_LOCK2
# define ANDROID_SYM_S_LOCK2 "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"
#endif
#ifndef ANDROID_SYM_S_UNLOCK
# define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
#endif
typedef void (*AndroidSurface_lock)(void *, void *, int);
typedef void (*AndroidSurface_lock2)(void *, void *, void *);
typedef void (*AndroidSurface_unlockAndPost)(void *);
typedef struct {
void *p_dl_handle;
void *p_surface_handle;
AndroidSurface_lock pf_lock;
AndroidSurface_lock2 pf_lock2;
AndroidSurface_unlockAndPost pf_unlockAndPost;
} NativeSurface;
static inline void *
NativeSurface_Load(const char *psz_lib, NativeSurface *p_ns)
{
void *p_lib = dlopen(psz_lib, RTLD_NOW);
if (!p_lib)
return NULL;
p_ns->pf_lock = (AndroidSurface_lock)(dlsym(p_lib, ANDROID_SYM_S_LOCK));
p_ns->pf_lock2 = (AndroidSurface_lock2)(dlsym(p_lib, ANDROID_SYM_S_LOCK2));
p_ns->pf_unlockAndPost =
(AndroidSurface_unlockAndPost)(dlsym(p_lib, ANDROID_SYM_S_UNLOCK));
if ((p_ns->pf_lock || p_ns->pf_lock2) && p_ns->pf_unlockAndPost)
return p_lib;