[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