1 /*****************************************************************************
2 * androidsurface.c: android video output using Surface Flinger
3 *****************************************************************************
4 * Copyright © 2011 VideoLAN
6 * Authors: Ming Hu <tewilove@gmail.com>
7 * Ludovic Fauvet <etix@l0cal.com>
8 * Sébastien Toque <xilasz@gmail.com>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_vout_display.h>
32 #include <vlc_picture_pool.h>
36 #ifndef ANDROID_SYM_S_LOCK
37 # define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb"
39 #ifndef ANDROID_SYM_S_LOCK2
40 # define ANDROID_SYM_S_LOCK2 "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"
42 #ifndef ANDROID_SYM_S_UNLOCK
43 # define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
46 /*****************************************************************************
48 *****************************************************************************/
49 #define CHROMA_TEXT N_("Chroma used")
50 #define CHROMA_LONGTEXT N_(\
51 "Force use of a specific chroma for output. Default is RGB32.")
53 #define CFG_PREFIX "androidsurface-"
55 static int Open (vlc_object_t *);
56 static void Close(vlc_object_t *);
59 set_category(CAT_VIDEO)
60 set_subcategory(SUBCAT_VIDEO_VOUT)
61 set_shortname("AndroidSurface")
62 set_description(N_("Android Surface video output"))
63 set_capability("vout display", 155)
64 add_shortcut("androidsurface", "android")
65 add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
66 set_callbacks(Open, Close)
69 /*****************************************************************************
71 *****************************************************************************/
73 extern void *jni_LockAndGetAndroidSurface();
74 extern void jni_UnlockAndroidSurface();
75 extern void jni_SetAndroidSurfaceSize(int width, int height, int sar_num, int sar_den);
77 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb
78 typedef void (*Surface_lock)(void *, void *, int);
79 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE
80 typedef void (*Surface_lock2)(void *, void *, void *);
81 // _ZN7android7Surface13unlockAndPostEv
82 typedef void (*Surface_unlockAndPost)(void *);
84 /*****************************************************************************
86 *****************************************************************************/
88 static picture_pool_t *Pool (vout_display_t *, unsigned);
89 static void Display(vout_display_t *, picture_t *, subpicture_t *);
90 static int Control(vout_display_t *, int, va_list);
93 typedef struct _SurfaceInfo {
100 uint32_t reserved[2];
104 struct vout_display_sys_t {
105 picture_pool_t *pool;
108 Surface_lock2 s_lock2;
109 Surface_unlockAndPost s_unlockAndPost;
111 picture_resource_t resource;
124 vout_display_sys_t *sys;
127 static int AndroidLockSurface(picture_t *);
128 static void AndroidUnlockSurface(picture_t *);
130 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
132 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys) {
133 void *p_library = dlopen(psz_lib, RTLD_NOW);
135 sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK));
136 sys->s_lock2 = (Surface_lock2)(dlsym(p_library, ANDROID_SYM_S_LOCK2));
137 sys->s_unlockAndPost =
138 (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK));
139 if ((sys->s_lock || sys->s_lock2) && sys->s_unlockAndPost) {
147 static void *InitLibrary(vout_display_sys_t *sys) {
149 if ((p_library = LoadSurface("libsurfaceflinger_client.so", sys)))
151 if ((p_library = LoadSurface("libgui.so", sys)))
153 return LoadSurface("libui.so", sys);
156 static int Open(vlc_object_t *p_this) {
157 vout_display_t *vd = (vout_display_t *)p_this;
158 vout_display_sys_t *sys;
162 if (vlc_mutex_trylock(&single_instance) != 0) {
163 msg_Err(vd, "Can't start more than one instance at a time");
167 /* Allocate structure */
168 sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
170 vlc_mutex_unlock(&single_instance);
175 sys->p_library = p_library = InitLibrary(sys);
178 msg_Err(vd, "Could not initialize libui.so/libgui.so/libsurfaceflinger_client.so!");
179 vlc_mutex_unlock(&single_instance);
184 video_format_t fmt = vd->fmt;
186 char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
188 fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
191 fmt.i_chroma = VLC_CODEC_RGB32;
193 switch(fmt.i_chroma) {
195 /* avoid swscale usage by asking for I420 instead since the
196 * vout already has code to swap the buffers */
197 fmt.i_chroma = VLC_CODEC_I420;
201 case VLC_CODEC_RGB16:
202 fmt.i_bmask = 0x0000001f;
203 fmt.i_gmask = 0x000007e0;
204 fmt.i_rmask = 0x0000f800;
207 case VLC_CODEC_RGB32:
208 fmt.i_rmask = 0x000000ff;
209 fmt.i_gmask = 0x0000ff00;
210 fmt.i_bmask = 0x00ff0000;
216 video_format_FixRgb(&fmt);
218 const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(fmt.i_chroma);
221 sys->pixel_size = desc->pixel_size;
223 msg_Dbg(vd, "Pixel format %4.4s (%d bpp)", (char*)&fmt.i_chroma, sys->pixel_size);
225 /* Create the associated picture */
226 picture_resource_t *rsc = &sys->resource;
227 rsc->p_sys = malloc(sizeof(*rsc->p_sys));
230 rsc->p_sys->sys = sys;
232 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
233 rsc->p[i].p_pixels = NULL;
234 rsc->p[i].i_pitch = 0;
235 rsc->p[i].i_lines = 0;
237 picture_t *picture = picture_NewFromResource(&fmt, rsc);
241 /* Wrap it into a picture pool */
242 picture_pool_configuration_t pool_cfg;
243 memset(&pool_cfg, 0, sizeof(pool_cfg));
244 pool_cfg.picture_count = 1;
245 pool_cfg.picture = &picture;
246 pool_cfg.lock = AndroidLockSurface;
247 pool_cfg.unlock = AndroidUnlockSurface;
249 sys->pool = picture_pool_NewExtended(&pool_cfg);
251 picture_Release(picture);
255 /* Setup vout_display */
259 vd->display = Display;
260 vd->control = Control;
264 /* Fix initial state */
265 vout_display_SendEventFullscreen(vd, false);
267 sys->i_sar_num = vd->source.i_sar_num;
268 sys->i_sar_den = vd->source.i_sar_den;
276 vlc_mutex_unlock(&single_instance);
280 static void Close(vlc_object_t *p_this) {
281 vout_display_t *vd = (vout_display_t *)p_this;
282 vout_display_sys_t *sys = vd->sys;
284 picture_pool_Delete(sys->pool);
285 dlclose(sys->p_library);
287 vlc_mutex_unlock(&single_instance);
290 static picture_pool_t *Pool(vout_display_t *vd, unsigned count) {
291 vout_display_sys_t *sys = vd->sys;
296 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
297 static void SetupPictureYV12( SurfaceInfo* p_surfaceInfo, picture_t *p_picture )
299 /* according to document of android.graphics.ImageFormat.YV12 */
300 int i_stride = ALIGN_16_PIXELS( p_surfaceInfo->s );
301 int i_c_stride = ALIGN_16_PIXELS( i_stride / 2 );
303 p_picture->p->i_pitch = i_stride;
305 /* Fill chroma planes for planar YUV */
306 for( int n = 1; n < p_picture->i_planes; n++ )
308 const plane_t *o = &p_picture->p[n-1];
309 plane_t *p = &p_picture->p[n];
311 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
312 p->i_pitch = i_c_stride;
313 p->i_lines = p_picture->format.i_height / 2;
316 if( vlc_fourcc_AreUVPlanesSwapped( p_picture->format.i_chroma,
318 uint8_t *p_tmp = p_picture->p[1].p_pixels;
319 p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
320 p_picture->p[2].p_pixels = p_tmp;
324 static int AndroidLockSurface(picture_t *picture) {
325 picture_sys_t *picsys = picture->p_sys;
326 vout_display_sys_t *sys = picsys->sys;
331 sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
332 sh = picture->p[0].i_visible_lines;
334 picsys->surf = surf = jni_LockAndGetAndroidSurface();
335 info = &(picsys->info);
337 if (unlikely(!surf)) {
338 jni_UnlockAndroidSurface();
343 sys->s_lock(surf, info, 1);
345 sys->s_lock2(surf, info, NULL);
347 // input size doesn't match the surface size,
349 if (info->w != sw || info->h != sh) {
350 jni_SetAndroidSurfaceSize(sw, sh, sys->i_sar_num, sys->i_sar_den);
351 sys->s_unlockAndPost(surf);
352 jni_UnlockAndroidSurface();
356 picture->p->p_pixels = (uint8_t*)info->bits;
357 picture->p->i_lines = info->h;
358 picture->p->i_pitch = sys->pixel_size * info->s;
360 if (info->format == 0x32315659 /*ANDROID_IMAGE_FORMAT_YV12*/)
361 SetupPictureYV12(info, picture);
366 static void AndroidUnlockSurface(picture_t *picture) {
367 picture_sys_t *picsys = picture->p_sys;
368 vout_display_sys_t *sys = picsys->sys;
370 if (likely(picsys->surf))
371 sys->s_unlockAndPost(picsys->surf);
372 jni_UnlockAndroidSurface();
375 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) {
377 VLC_UNUSED(subpicture);
378 picture_Release(picture);
381 static int Control(vout_display_t *vd, int query, va_list args) {
385 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
386 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
387 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
388 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
389 case VOUT_DISPLAY_CHANGE_ZOOM:
390 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
391 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
392 case VOUT_DISPLAY_GET_OPENGL:
394 case VOUT_DISPLAY_HIDE_MOUSE:
397 msg_Err(vd, "Unknown request in android vout display");