]> git.sesse.net Git - vlc/blob - modules/video_output/androidsurface.c
androidsurface: setup format depending on surface format
[vlc] / modules / video_output / androidsurface.c
1 /*****************************************************************************
2  * androidsurface.c: android video output using Surface Flinger
3  *****************************************************************************
4  * Copyright © 2011 VideoLAN
5  *
6  * Authors: Ming Hu <tewilove@gmail.com>
7  *          Ludovic Fauvet <etix@l0cal.com>
8  *          Sébastien Toque <xilasz@gmail.com>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_vout_display.h>
32 #include <vlc_picture_pool.h>
33
34 #include <dlfcn.h>
35
36 #ifndef ANDROID_SYM_S_LOCK
37 # define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb"
38 #endif
39 #ifndef ANDROID_SYM_S_LOCK2
40 # define ANDROID_SYM_S_LOCK2 "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"
41 #endif
42 #ifndef ANDROID_SYM_S_UNLOCK
43 # define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
44 #endif
45
46 /*****************************************************************************
47  * Module descriptor
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.")
52
53 #define CFG_PREFIX "androidsurface-"
54
55 static int  Open (vlc_object_t *);
56 static void Close(vlc_object_t *);
57
58 vlc_module_begin()
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)
67 vlc_module_end()
68
69 /*****************************************************************************
70  * JNI prototypes
71  *****************************************************************************/
72
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);
76
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 *);
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87
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);
91
92 /* */
93 typedef struct _SurfaceInfo {
94     uint32_t    w;
95     uint32_t    h;
96     uint32_t    s;
97     uint32_t    usage;
98     uint32_t    format;
99     uint32_t*   bits;
100     uint32_t    reserved[2];
101 } SurfaceInfo;
102
103 /* */
104 struct vout_display_sys_t {
105     picture_pool_t *pool;
106     void *p_library;
107     Surface_lock s_lock;
108     Surface_lock2 s_lock2;
109     Surface_unlockAndPost s_unlockAndPost;
110
111     picture_resource_t resource;
112
113     int pixel_size;
114
115     /* density */
116     int i_sar_num;
117     int i_sar_den;
118 };
119
120 struct picture_sys_t
121 {
122     void *surf;
123     SurfaceInfo info;
124     vout_display_sys_t *sys;
125 };
126
127 static int  AndroidLockSurface(picture_t *);
128 static void AndroidUnlockSurface(picture_t *);
129
130 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
131
132 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys) {
133     void *p_library = dlopen(psz_lib, RTLD_NOW);
134     if (p_library) {
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) {
140             return p_library;
141         }
142         dlclose(p_library);
143     }
144     return NULL;
145 }
146
147 static void *InitLibrary(vout_display_sys_t *sys) {
148     void *p_library;
149     if ((p_library = LoadSurface("libsurfaceflinger_client.so", sys)))
150         return p_library;
151     if ((p_library = LoadSurface("libgui.so", sys)))
152         return p_library;
153     return LoadSurface("libui.so", sys);
154 }
155
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;
159     void *p_library;
160
161     /* */
162     if (vlc_mutex_trylock(&single_instance) != 0) {
163         msg_Err(vd, "Can't start more than one instance at a time");
164         return VLC_EGENERIC;
165     }
166
167     /* Allocate structure */
168     sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
169     if (!sys) {
170         vlc_mutex_unlock(&single_instance);
171         return VLC_ENOMEM;
172     }
173
174     /* */
175     sys->p_library = p_library = InitLibrary(sys);
176     if (!p_library) {
177         free(sys);
178         msg_Err(vd, "Could not initialize libui.so/libgui.so/libsurfaceflinger_client.so!");
179         vlc_mutex_unlock(&single_instance);
180         return VLC_EGENERIC;
181     }
182
183     /* Setup chroma */
184     video_format_t fmt = vd->fmt;
185
186     char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
187     if( psz_fcc ) {
188         fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
189         free(psz_fcc);
190     } else
191         fmt.i_chroma = VLC_CODEC_RGB32;
192
193     switch(fmt.i_chroma) {
194         case VLC_CODEC_YV12:
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;
198         case VLC_CODEC_I420:
199             break;
200
201         case VLC_CODEC_RGB16:
202             fmt.i_bmask = 0x0000001f;
203             fmt.i_gmask = 0x000007e0;
204             fmt.i_rmask = 0x0000f800;
205             break;
206
207         case VLC_CODEC_RGB32:
208             fmt.i_rmask  = 0x000000ff;
209             fmt.i_gmask  = 0x0000ff00;
210             fmt.i_bmask  = 0x00ff0000;
211             break;
212
213         default:
214             return VLC_EGENERIC;
215     }
216     video_format_FixRgb(&fmt);
217
218     const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(fmt.i_chroma);
219     if (unlikely(!desc))
220         return VLC_EGENERIC;
221     sys->pixel_size = desc->pixel_size;
222
223     msg_Dbg(vd, "Pixel format %4.4s (%d bpp)", (char*)&fmt.i_chroma, sys->pixel_size);
224
225     /* Create the associated picture */
226     picture_resource_t *rsc = &sys->resource;
227     rsc->p_sys = malloc(sizeof(*rsc->p_sys));
228     if (!rsc->p_sys)
229         goto enomem;
230     rsc->p_sys->sys = sys;
231
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;
236     }
237     picture_t *picture = picture_NewFromResource(&fmt, rsc);
238     if (!picture)
239         goto enomem;
240
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;
248
249     sys->pool = picture_pool_NewExtended(&pool_cfg);
250     if (!sys->pool) {
251         picture_Release(picture);
252         goto enomem;
253     }
254
255     /* Setup vout_display */
256     vd->sys     = sys;
257     vd->fmt     = fmt;
258     vd->pool    = Pool;
259     vd->display = Display;
260     vd->control = Control;
261     vd->prepare = NULL;
262     vd->manage  = NULL;
263
264     /* Fix initial state */
265     vout_display_SendEventFullscreen(vd, false);
266
267     sys->i_sar_num = vd->source.i_sar_num;
268     sys->i_sar_den = vd->source.i_sar_den;
269
270     return VLC_SUCCESS;
271
272 enomem:
273     free(rsc->p_sys);
274     free(sys);
275     dlclose(p_library);
276     vlc_mutex_unlock(&single_instance);
277     return VLC_ENOMEM;
278 }
279
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;
283
284     picture_pool_Delete(sys->pool);
285     dlclose(sys->p_library);
286     free(sys);
287     vlc_mutex_unlock(&single_instance);
288 }
289
290 static picture_pool_t *Pool(vout_display_t *vd, unsigned count) {
291     vout_display_sys_t *sys = vd->sys;
292     VLC_UNUSED(count);
293     return sys->pool;
294 }
295
296 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
297 static void SetupPictureYV12( SurfaceInfo* p_surfaceInfo, picture_t *p_picture )
298 {
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 );
302
303     p_picture->p->i_pitch = i_stride;
304
305     /* Fill chroma planes for planar YUV */
306     for( int n = 1; n < p_picture->i_planes; n++ )
307     {
308         const plane_t *o = &p_picture->p[n-1];
309         plane_t *p = &p_picture->p[n];
310
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;
314     }
315
316     if( vlc_fourcc_AreUVPlanesSwapped( p_picture->format.i_chroma,
317                                        VLC_CODEC_YV12 ) ) {
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;
321     }
322 }
323
324 static int  AndroidLockSurface(picture_t *picture) {
325     picture_sys_t *picsys = picture->p_sys;
326     vout_display_sys_t *sys = picsys->sys;
327     SurfaceInfo *info;
328     uint32_t sw, sh;
329     void *surf;
330
331     sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
332     sh = picture->p[0].i_visible_lines;
333
334     picsys->surf = surf = jni_LockAndGetAndroidSurface();
335     info = &(picsys->info);
336
337     if (unlikely(!surf)) {
338         jni_UnlockAndroidSurface();
339         return VLC_EGENERIC;
340     }
341
342     if (sys->s_lock)
343         sys->s_lock(surf, info, 1);
344     else
345         sys->s_lock2(surf, info, NULL);
346
347     // input size doesn't match the surface size,
348     // request a resize
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();
353         return VLC_EGENERIC;
354     }
355
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;
359
360     if (info->format == 0x32315659 /*ANDROID_IMAGE_FORMAT_YV12*/)
361         SetupPictureYV12(info, picture);
362
363     return VLC_SUCCESS;
364 }
365
366 static void AndroidUnlockSurface(picture_t *picture) {
367     picture_sys_t *picsys = picture->p_sys;
368     vout_display_sys_t *sys = picsys->sys;
369
370     if (likely(picsys->surf))
371         sys->s_unlockAndPost(picsys->surf);
372     jni_UnlockAndroidSurface();
373 }
374
375 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) {
376     VLC_UNUSED(vd);
377     VLC_UNUSED(subpicture);
378     picture_Release(picture);
379 }
380
381 static int Control(vout_display_t *vd, int query, va_list args) {
382     VLC_UNUSED(args);
383
384     switch (query) {
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:
393             return VLC_EGENERIC;
394         case VOUT_DISPLAY_HIDE_MOUSE:
395             return VLC_SUCCESS;
396         default:
397             msg_Err(vd, "Unknown request in android vout display");
398             return VLC_EGENERIC;
399     }
400 }