[vlc-devel] [PoC] Android Surface video output
Rémi Denis-Courmont
remi at remlab.net
Wed Jun 1 22:25:18 CEST 2011
Inline.
> /*****************************************************************************
> * androidsurface.c: android video output using Surface Flinger
> *****************************************************************************
> * Copyright � 2011 VideoLAN
Encoding?
> *
> * Authors: Ming Hu <tewilove at gmail.com>
> * Ludovic Fauvet <etix at l0cal.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
Vomitive but apparently unavoidable.
>
> /*****************************************************************************
> * 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", 250)
Not according to the "guidelines" set in 73d27a4e.
> add_shortcut("androidsurface", "android")
> set_callbacks(Open, Close)
> vlc_module_end()
>
> /*****************************************************************************
> * JNI prototypes
> *****************************************************************************/
>
> void *LockAndGetSurface();
> void UnlockSurface();
> void SetSurfaceSize(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;
>
> uint32_t sw;
> uint32_t sh;
> };
>
> /* */
> typedef struct _SurfaceInfo {
> uint32_t w;
> uint32_t h;
> uint32_t s;
> uint32_t a;
> uint32_t b;
> uint32_t c;
> uint32_t reserved[2];
> } SurfaceInfo;
>
> static Surface_lock s_lock = NULL;
> static Surface_unlockAndPost s_unlockAndPost = NULL;
>
> #define OPENLIB(SO) \
> p_library = dlopen(SO, RTLD_NOW); \
> if (p_library) { \
> s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK)); \
> s_unlockAndPost = (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK)); \
> if (s_lock && s_unlockAndPost) { \
> return p_library; \
> } \
> dlclose(p_library); \
> }
>
> static void *InitLibrary() {
> void *p_library;
> OPENLIB("libsurfaceflinger_client.so");
> OPENLIB("libui.so");
> return NULL;
> }
>
> #undef OPENLIB
>
> static int Open(vlc_object_t *p_this) {
> vout_display_t *vd = (vout_display_t *)p_this;
> vout_display_sys_t *sys;
> SurfaceInfo info;
> void *surf;
> void *p_library;
>
> /* */
> p_library = InitLibrary();
> if (!p_library) {
> msg_Err(vd, "Could not initialize libui.so/libsurfaceflinger_client.so!");
> return VLC_EGENERIC;
> }
This is not re-entrant.
>
> /* Get the geometry of the Android surface */
> surf = LockAndGetSurface();
> if (!surf) {
> UnlockSurface();
> dlclose(p_library);
> msg_Err(vd, "No surface is attached!");
> return VLC_EGENERIC;
> }
> s_lock(surf, &info, 1);
> s_unlockAndPost(surf);
> UnlockSurface();
>
> /* Setup chroma */
> const char *chroma_format = "RV32";
> vlc_fourcc_t chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, chroma_format);
I don't see the point of that if chroma is constant.
> if (!chroma) {
> dlclose(p_library);
> msg_Err(vd, "Unsupported chroma format %s", chroma_format);
> return VLC_EGENERIC;
> }
>
> video_format_t fmt = vd->fmt;
> fmt.i_chroma = chroma;
> fmt.i_rmask = 0x000000ff;
> fmt.i_gmask = 0x0000ff00;
> fmt.i_bmask = 0x00ff0000;
> video_format_FixRgb(&fmt);
>
> /* Allocate structure */
> sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
> if (!sys) {
> dlclose(p_library);
> return VLC_ENOMEM;
> }
> sys->p_library = p_library;
>
> /* 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);
> vout_display_SendEventDisplaySize(vd, info.w, info.h, false);
>
> return VLC_SUCCESS;
> }
>
> 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->pool)
> picture_pool_Delete(sys->pool);
> dlclose(sys->p_library);
> free(sys);
> }
>
> static picture_pool_t *Pool(vout_display_t *vd, unsigned count) {
> vout_display_sys_t *sys = vd->sys;
>
> if (!sys->pool)
> sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
Does it really make sense to have more than one picture in this case?
> return sys->pool;
> }
>
> static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) {
> VLC_UNUSED(subpicture);
> vout_display_sys_t *sys = vd->sys;
> SurfaceInfo info;
> void *surf;
> uint32_t sw, sh;
>
> if (picture->i_planes != 1)
> goto out;
I believe this cannot happen.
>
> //XXX This doesn't handle all cases
> sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
> sh = picture->p[0].i_visible_lines;
>
> surf = LockAndGetSurface();
In principles, this should be done by the picture Lock callback. Otherwise, it would be better in Prepare than in Display, except for "post".
> if (likely(surf)) {
> s_lock(surf, &info, 1);
>
> if (sw != sys->sw || sh != sys->sh) {
> sys->sw = sw;
> sys->sh = sh;
> SetSurfaceSize(sw, sh);
> }
>
> if (info.w == sw && info.h == sh) {
> memcpy((uint32_t*)(info.s >= info.w ? info.c : info.b),
> (uint32_t*)(picture->p[0].p_pixels),
> sw * sh * 4);
> }
>
> s_unlockAndPost(surf);
> }
> UnlockSurface();
> out:
> 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;
> }
> }
--
Rémi Denis-Courmont
http://www.remlab.net/
http://fi.linkedin.com/in/remidenis
More information about the vlc-devel
mailing list