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 *****************************************************************************/
50 static int Open (vlc_object_t *);
51 static void Close(vlc_object_t *);
54 set_category(CAT_VIDEO)
55 set_subcategory(SUBCAT_VIDEO_VOUT)
56 set_shortname("AndroidSurface")
57 set_description(N_("Android Surface video output"))
58 set_capability("vout display", 155)
59 add_shortcut("androidsurface", "android")
60 set_callbacks(Open, Close)
63 /*****************************************************************************
65 *****************************************************************************/
67 extern void *jni_LockAndGetAndroidSurface();
68 extern void jni_UnlockAndroidSurface();
69 extern void jni_SetAndroidSurfaceSize(int width, int height);
71 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb
72 typedef void (*Surface_lock)(void *, void *, int);
73 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE
74 typedef void (*Surface_lock2)(void *, void *, void *);
75 // _ZN7android7Surface13unlockAndPostEv
76 typedef void (*Surface_unlockAndPost)(void *);
78 /*****************************************************************************
80 *****************************************************************************/
82 static picture_pool_t *Pool (vout_display_t *, unsigned);
83 static void Display(vout_display_t *, picture_t *, subpicture_t *);
84 static int Control(vout_display_t *, int, va_list);
87 struct vout_display_sys_t {
91 Surface_lock2 s_lock2;
92 Surface_unlockAndPost s_unlockAndPost;
94 picture_resource_t resource;
98 typedef struct _SurfaceInfo {
105 uint32_t reserved[2];
112 vout_display_sys_t *sys;
115 static int AndroidLockSurface(picture_t *);
116 static void AndroidUnlockSurface(picture_t *);
118 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
120 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys) {
121 void *p_library = dlopen(psz_lib, RTLD_NOW);
123 sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK));
124 sys->s_lock2 = (Surface_lock2)(dlsym(p_library, ANDROID_SYM_S_LOCK2));
125 sys->s_unlockAndPost =
126 (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK));
127 if ((sys->s_lock || sys->s_lock2) && sys->s_unlockAndPost) {
135 static void *InitLibrary(vout_display_sys_t *sys) {
137 if ((p_library = LoadSurface("libsurfaceflinger_client.so", sys)))
139 if ((p_library = LoadSurface("libgui.so", sys)))
141 return LoadSurface("libui.so", sys);
144 static int Open(vlc_object_t *p_this) {
145 vout_display_t *vd = (vout_display_t *)p_this;
146 vout_display_sys_t *sys;
150 if (vlc_mutex_trylock(&single_instance) != 0) {
151 msg_Err(vd, "Can't start more than one instance at a time");
155 /* Allocate structure */
156 sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
158 vlc_mutex_unlock(&single_instance);
163 sys->p_library = p_library = InitLibrary(sys);
166 msg_Err(vd, "Could not initialize libui.so/libgui.so/libsurfaceflinger_client.so!");
167 vlc_mutex_unlock(&single_instance);
172 video_format_t fmt = vd->fmt;
173 fmt.i_chroma = VLC_CODEC_RGB32;
174 fmt.i_rmask = 0x000000ff;
175 fmt.i_gmask = 0x0000ff00;
176 fmt.i_bmask = 0x00ff0000;
177 video_format_FixRgb(&fmt);
179 /* Create the associated picture */
180 picture_resource_t *rsc = &sys->resource;
181 rsc->p_sys = malloc(sizeof(*rsc->p_sys));
184 rsc->p_sys->sys = sys;
186 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
187 rsc->p[i].p_pixels = NULL;
188 rsc->p[i].i_pitch = 0;
189 rsc->p[i].i_lines = 0;
191 picture_t *picture = picture_NewFromResource(&fmt, rsc);
195 /* Wrap it into a picture pool */
196 picture_pool_configuration_t pool_cfg;
197 memset(&pool_cfg, 0, sizeof(pool_cfg));
198 pool_cfg.picture_count = 1;
199 pool_cfg.picture = &picture;
200 pool_cfg.lock = AndroidLockSurface;
201 pool_cfg.unlock = AndroidUnlockSurface;
203 sys->pool = picture_pool_NewExtended(&pool_cfg);
205 picture_Release(picture);
209 /* Setup vout_display */
213 vd->display = Display;
214 vd->control = Control;
218 /* Fix initial state */
219 vout_display_SendEventFullscreen(vd, false);
227 vlc_mutex_unlock(&single_instance);
231 static void Close(vlc_object_t *p_this) {
232 vout_display_t *vd = (vout_display_t *)p_this;
233 vout_display_sys_t *sys = vd->sys;
235 picture_pool_Delete(sys->pool);
236 dlclose(sys->p_library);
238 vlc_mutex_unlock(&single_instance);
241 static picture_pool_t *Pool(vout_display_t *vd, unsigned count) {
242 vout_display_sys_t *sys = vd->sys;
247 static int AndroidLockSurface(picture_t *picture) {
248 picture_sys_t *picsys = picture->p_sys;
249 vout_display_sys_t *sys = picsys->sys;
254 sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
255 sh = picture->p[0].i_visible_lines;
257 picsys->surf = surf = jni_LockAndGetAndroidSurface();
258 info = &(picsys->info);
260 if (unlikely(!surf)) {
261 jni_UnlockAndroidSurface();
266 sys->s_lock(surf, info, 1);
268 sys->s_lock2(surf, info, NULL);
270 // input size doesn't match the surface size,
272 if (info->w != sw || info->h != sh) {
273 jni_SetAndroidSurfaceSize(sw, sh);
274 sys->s_unlockAndPost(surf);
275 jni_UnlockAndroidSurface();
279 picture->p->p_pixels = (uint8_t*)info->bits;
280 picture->p->i_pitch = 4 * info->s;
281 picture->p->i_lines = info->h;
286 static void AndroidUnlockSurface(picture_t *picture) {
287 picture_sys_t *picsys = picture->p_sys;
288 vout_display_sys_t *sys = picsys->sys;
290 if (likely(picsys->surf))
291 sys->s_unlockAndPost(picsys->surf);
292 jni_UnlockAndroidSurface();
295 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) {
297 VLC_UNUSED(subpicture);
298 picture_Release(picture);
301 static int Control(vout_display_t *vd, int query, va_list args) {
305 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
306 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
307 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
308 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
309 case VOUT_DISPLAY_CHANGE_ZOOM:
310 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
311 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
312 case VOUT_DISPLAY_GET_OPENGL:
314 case VOUT_DISPLAY_HIDE_MOUSE:
317 msg_Err(vd, "Unknown request in android vout display");