From: Jean-Baptiste Kempf Date: Thu, 9 Jun 2011 22:34:23 +0000 (+0200) Subject: Android video output based on Surface X-Git-Tag: 1.2.0-pre1~2208 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=6ca901ec6e8844c4b1b26a0da96900bd4bf1f569;p=vlc Android video output based on Surface Collective work by Ming Hu, Ludovic Fauvet, Sébastien Toque and minor fixes by Jean-Baptiste Kempf Signed-off-by: Jean-Baptiste Kempf --- diff --git a/NEWS b/NEWS index 2a19dccd71..d3fb2d4fac 100644 --- a/NEWS +++ b/NEWS @@ -79,6 +79,7 @@ Video Output: * Various vmem improvements * OpenGL video output now accepts YUV as input and uses fragment programs for chroma conversion + * New video output for Android platform, based on Surface Audio Output: * New audio output based on AudioQueue API for iOS diff --git a/configure.ac b/configure.ac index bb14828500..f9ee893416 100644 --- a/configure.ac +++ b/configure.ac @@ -3318,6 +3318,19 @@ dnl SVG module dnl PKG_ENABLE_MODULES_VLC([SVG], [], [librsvg-2.0 >= 2.9.0], [SVG rendering 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 + if test "${HAVE_ANDROID}" = "1"; then + VLC_ADD_PLUGIN([android_surface]) + VLC_ADD_LDFLAGS([android_surface], [-ldl]) + fi +fi + + dnl dnl iOS vout module dnl diff --git a/extras/package/android/configure.sh b/extras/package/android/configure.sh index 888750c76f..2f90145047 100755 --- a/extras/package/android/configure.sh +++ b/extras/package/android/configure.sh @@ -92,7 +92,7 @@ sh $VLC_SOURCEDIR/configure --host=arm-eabi-linux --build=x86_64-unknown-linux $ --disable-sqlite \ --disable-udev \ --disable-libxml2 \ - --enable-android-vout \ + --enable-android-surface \ --disable-caca \ --disable-glx \ --disable-egl \ diff --git a/modules/LIST b/modules/LIST index b53f656a63..b085295081 100644 --- a/modules/LIST +++ b/modules/LIST @@ -42,6 +42,7 @@ $Id$ * alphamask: Alpha layer mask video filter * alsa: audio output module using the ALSA API * amem: audio memory output + * android_surface: video output for Android, based on Surface * antiflicker: anti-flicker video filter * aout_directx: audio output module using the DirectX API * aout_file: Audio output to write to a file diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am index 724cfeb8b8..c3f884a70a 100644 --- a/modules/video_output/Modules.am +++ b/modules/video_output/Modules.am @@ -13,7 +13,7 @@ SOURCES_vmem = vmem.c SOURCES_yuv = yuv.c SOURCES_vout_macosx = macosx.m opengl.h opengl.c SOURCES_vout_ios = ios.m opengl.h opengl.c - +SOURCES_android_surface = androidsurface.c ### OpenGL ### # TODO: merge all three source files (?) diff --git a/modules/video_output/androidsurface.c b/modules/video_output/androidsurface.c new file mode 100644 index 0000000000..9bfd3131ee --- /dev/null +++ b/modules/video_output/androidsurface.c @@ -0,0 +1,308 @@ +/***************************************************************************** + * androidsurface.c: android video output using Surface Flinger + ***************************************************************************** + * Copyright © 2011 VideoLAN + * + * Authors: Ming Hu + * Ludovic Fauvet + * Sébastien Toque + * + * This library 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 library 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 General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include + +#ifndef ANDROID_SYM_S_LOCK +# define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb" +#endif +#ifndef ANDROID_SYM_S_UNLOCK +# define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv" +#endif + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ + +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") + set_callbacks(Open, Close) +vlc_module_end() + +/***************************************************************************** + * JNI prototypes + *****************************************************************************/ + +extern void *jni_LockAndGetAndroidSurface(); +extern void jni_UnlockAndroidSurface(); +extern void jni_SetAndroidSurfaceSize(int width, int height); + +// _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb +typedef void (*Surface_lock)(void *, void *, int); +// _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); + +/* */ +struct vout_display_sys_t { + picture_pool_t *pool; + void *p_library; + Surface_lock s_lock; + Surface_unlockAndPost s_unlockAndPost; + + picture_resource_t resource; +}; + +/* */ +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 picture_sys_t +{ + void *surf; + SurfaceInfo info; + vout_display_sys_t *sys; +}; + +static int AndroidLockSurface(picture_t *); +static void AndroidUnlockSurface(picture_t *); + +static vlc_mutex_t single_instance = VLC_STATIC_MUTEX; + +static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys) { + void *p_library = dlopen(psz_lib, RTLD_NOW); + if (p_library) { + sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK)); + sys->s_unlockAndPost = + (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK)); + if (sys->s_lock && sys->s_unlockAndPost) { + return p_library; + } + dlclose(p_library); + } + return NULL; +} + +static void *InitLibrary(vout_display_sys_t *sys) { + void *p_library; + if ((p_library = LoadSurface("libsurfaceflinger_client.so", sys))) + return p_library; + return LoadSurface("libui.so", sys); +} + +static int Open(vlc_object_t *p_this) { + vout_display_t *vd = (vout_display_t *)p_this; + vout_display_sys_t *sys; + void *p_library; + + /* */ + if (vlc_mutex_trylock(&single_instance) != 0) { + msg_Err(vd, "Can't start more than one instance at a time"); + return VLC_EGENERIC; + } + + /* Allocate structure */ + sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys)); + if (!sys) { + vlc_mutex_unlock(&single_instance); + return VLC_ENOMEM; + } + + /* */ + sys->p_library = p_library = InitLibrary(sys); + if (!p_library) { + free(sys); + msg_Err(vd, "Could not initialize libui.so/libsurfaceflinger_client.so!"); + vlc_mutex_unlock(&single_instance); + return VLC_EGENERIC; + } + + /* Setup chroma */ + video_format_t fmt = vd->fmt; + fmt.i_chroma = VLC_CODEC_RGB32; + fmt.i_rmask = 0x000000ff; + fmt.i_gmask = 0x0000ff00; + fmt.i_bmask = 0x00ff0000; + video_format_FixRgb(&fmt); + + /* Create the associated picture */ + picture_resource_t *rsc = &sys->resource; + rsc->p_sys = malloc(sizeof(*rsc->p_sys)); + if (!rsc->p_sys) + goto enomem; + rsc->p_sys->sys = sys; + + for (int i = 0; i < PICTURE_PLANE_MAX; i++) { + rsc->p[i].p_pixels = NULL; + rsc->p[i].i_pitch = 0; + rsc->p[i].i_lines = 0; + } + picture_t *picture = picture_NewFromResource(&fmt, rsc); + if (!picture) + goto enomem; + + /* 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 enomem; + } + + /* Setup vout_display */ + vd->sys = sys; + vd->fmt = fmt; + vd->pool = Pool; + vd->display = Display; + vd->control = Control; + vd->prepare = NULL; + vd->manage = NULL; + + /* Fix initial state */ + vout_display_SendEventFullscreen(vd, false); + + return VLC_SUCCESS; + +enomem: + free(rsc->p_sys); + free(sys); + dlclose(p_library); + vlc_mutex_unlock(&single_instance); + 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; + + picture_pool_Delete(sys->pool); + dlclose(sys->p_library); + free(sys); + vlc_mutex_unlock(&single_instance); +} + +static picture_pool_t *Pool(vout_display_t *vd, unsigned count) { + vout_display_sys_t *sys = vd->sys; + VLC_UNUSED(count); + return sys->pool; +} + +static int AndroidLockSurface(picture_t *picture) { + picture_sys_t *picsys = picture->p_sys; + vout_display_sys_t *sys = picsys->sys; + SurfaceInfo *info; + uint32_t sw, sh; + void *surf; + + sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch; + sh = picture->p[0].i_visible_lines; + + picsys->surf = surf = jni_LockAndGetAndroidSurface(); + info = &(picsys->info); + + if (unlikely(!surf)) { + jni_UnlockAndroidSurface(); + return VLC_EGENERIC; + } + + sys->s_lock(surf, info, 1); + + // input size doesn't match the surface size, + // request a resize + if (info->w != sw || info->h != sh) { + jni_SetAndroidSurfaceSize(sw, sh); + sys->s_unlockAndPost(surf); + jni_UnlockAndroidSurface(); + return VLC_EGENERIC; + } + + picture->p->p_pixels = (uint8_t*)info->bits; + picture->p->i_pitch = 4 * info->s; + picture->p->i_lines = info->h; + + return VLC_SUCCESS; +} + +static void AndroidUnlockSurface(picture_t *picture) { + picture_sys_t *picsys = picture->p_sys; + vout_display_sys_t *sys = picsys->sys; + + if (likely(picsys->surf)) + sys->s_unlockAndPost(picsys->surf); + jni_UnlockAndroidSurface(); +} + +static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { + VLC_UNUSED(vd); + VLC_UNUSED(subpicture); + picture_Release(picture); +} + +static int Control(vout_display_t *vd, int query, va_list args) { + VLC_UNUSED(args); + + switch (query) { + 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: + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: + case VOUT_DISPLAY_GET_OPENGL: + return VLC_EGENERIC; + case VOUT_DISPLAY_HIDE_MOUSE: + return VLC_SUCCESS; + default: + msg_Err(vd, "Unknown request in android vout display"); + return VLC_EGENERIC; + } +}