]> git.sesse.net Git - vlc/commitdiff
Android video output based on Surface
authorJean-Baptiste Kempf <jb@videolan.org>
Thu, 9 Jun 2011 22:34:23 +0000 (00:34 +0200)
committerJean-Baptiste Kempf <jb@videolan.org>
Tue, 14 Jun 2011 22:45:42 +0000 (00:45 +0200)
Collective work by Ming Hu, Ludovic Fauvet, Sébastien Toque and minor
fixes by Jean-Baptiste Kempf

Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
NEWS
configure.ac
extras/package/android/configure.sh
modules/LIST
modules/video_output/Modules.am
modules/video_output/androidsurface.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 2a19dccd71743cf6ef6ef3043ec3e263e1de5053..d3fb2d4fac19087f73f1b59f2260a77e9d55e25e 100644 (file)
--- 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
index bb14828500bfd9f6858e00144c821b5d6dacf73f..f9ee893416f1b48d148bb8f9be6ea858dfdee7d1 100644 (file)
@@ -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
index 888750c76f1c426830c483d9f137fcf013029721..2f90145047ee3ead909c74f553b4d7abcc6e6551 100755 (executable)
@@ -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 \
index b53f656a631bdca2b6c05cfdd0c1813382e1502a..b0852950815b1cf5b6ff7b61b042f336a1084d2f 100644 (file)
@@ -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
index 724cfeb8b8fbbe8d93be92d9ed8b78744d9596e7..c3f884a70a7c5ab8519805f3db64ae7efd0181f7 100644 (file)
@@ -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 (file)
index 0000000..9bfd313
--- /dev/null
@@ -0,0 +1,308 @@
+/*****************************************************************************
+ * 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 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 <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+#include <vlc_picture_pool.h>
+
+#include <dlfcn.h>
+
+#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;
+    }
+}