]> git.sesse.net Git - vlc/blob - modules/video_output/androidsurface.c
New AVFoundation video capture module for Mac OS X
[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     /* density */
114     int i_sar_num;
115     int i_sar_den;
116 };
117
118 struct picture_sys_t {
119     void *surf;
120     SurfaceInfo info;
121     vout_display_sys_t *sys;
122 };
123
124 static int  AndroidLockSurface(picture_t *);
125 static void AndroidUnlockSurface(picture_t *);
126
127 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
128
129 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys)
130 {
131     void *p_library = dlopen(psz_lib, RTLD_NOW);
132     if (!p_library)
133         return NULL;
134
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
140     if ((sys->s_lock || sys->s_lock2) && sys->s_unlockAndPost)
141         return p_library;
142
143     dlclose(p_library);
144     return NULL;
145 }
146
147 static void *InitLibrary(vout_display_sys_t *sys)
148 {
149     static const char *libs[] = {
150         "libsurfaceflinger_client.so",
151         "libgui.so",
152         "libui.so"
153     };
154
155     for (size_t i = 0; i < sizeof(libs) / sizeof(*libs); i++) {
156         void *lib = LoadSurface(libs[i], sys);
157         if (lib)
158             return lib;
159     }
160     return NULL;
161 }
162
163 static int Open(vlc_object_t *p_this)
164 {
165     vout_display_t *vd = (vout_display_t *)p_this;
166
167     /* */
168     if (vlc_mutex_trylock(&single_instance) != 0) {
169         msg_Err(vd, "Can't start more than one instance at a time");
170         return VLC_EGENERIC;
171     }
172
173     /* Allocate structure */
174     vout_display_sys_t *sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
175     if (!sys) {
176         vlc_mutex_unlock(&single_instance);
177         return VLC_ENOMEM;
178     }
179
180     /* */
181     sys->p_library = InitLibrary(sys);
182     if (!sys->p_library) {
183         free(sys);
184         msg_Err(vd, "Could not initialize libui.so/libgui.so/libsurfaceflinger_client.so!");
185         vlc_mutex_unlock(&single_instance);
186         return VLC_EGENERIC;
187     }
188
189     /* Setup chroma */
190     video_format_t fmt = vd->fmt;
191
192     char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
193     if( psz_fcc ) {
194         fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
195         free(psz_fcc);
196     } else
197         fmt.i_chroma = VLC_CODEC_RGB32;
198
199     switch(fmt.i_chroma) {
200         case VLC_CODEC_YV12:
201             /* avoid swscale usage by asking for I420 instead since the
202              * vout already has code to swap the buffers */
203             fmt.i_chroma = VLC_CODEC_I420;
204         case VLC_CODEC_I420:
205             break;
206
207         case VLC_CODEC_RGB16:
208             fmt.i_bmask = 0x0000001f;
209             fmt.i_gmask = 0x000007e0;
210             fmt.i_rmask = 0x0000f800;
211             break;
212
213         case VLC_CODEC_RGB32:
214             fmt.i_rmask  = 0x000000ff;
215             fmt.i_gmask  = 0x0000ff00;
216             fmt.i_bmask  = 0x00ff0000;
217             break;
218
219         default:
220             return VLC_EGENERIC;
221     }
222     video_format_FixRgb(&fmt);
223
224     msg_Dbg(vd, "Pixel format %4.4s", (char*)&fmt.i_chroma);
225
226     /* Create the associated picture */
227     picture_resource_t *rsc = &sys->resource;
228     rsc->p_sys = malloc(sizeof(*rsc->p_sys));
229     if (!rsc->p_sys)
230         goto enomem;
231     rsc->p_sys->sys = sys;
232
233     for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
234         rsc->p[i].p_pixels = NULL;
235         rsc->p[i].i_pitch = 0;
236         rsc->p[i].i_lines = 0;
237     }
238     picture_t *picture = picture_NewFromResource(&fmt, rsc);
239     if (!picture)
240         goto enomem;
241
242     /* Wrap it into a picture pool */
243     picture_pool_configuration_t pool_cfg;
244     memset(&pool_cfg, 0, sizeof(pool_cfg));
245     pool_cfg.picture_count = 1;
246     pool_cfg.picture       = &picture;
247     pool_cfg.lock          = AndroidLockSurface;
248     pool_cfg.unlock        = AndroidUnlockSurface;
249
250     sys->pool = picture_pool_NewExtended(&pool_cfg);
251     if (!sys->pool) {
252         picture_Release(picture);
253         goto enomem;
254     }
255
256     /* Setup vout_display */
257     vd->sys     = sys;
258     vd->fmt     = fmt;
259     vd->pool    = Pool;
260     vd->display = Display;
261     vd->control = Control;
262     vd->prepare = NULL;
263     vd->manage  = NULL;
264
265     /* Fix initial state */
266     vout_display_SendEventFullscreen(vd, false);
267
268     sys->i_sar_num = vd->source.i_sar_num;
269     sys->i_sar_den = vd->source.i_sar_den;
270
271     return VLC_SUCCESS;
272
273 enomem:
274     free(rsc->p_sys);
275     dlclose(sys->p_library);
276     free(sys);
277     vlc_mutex_unlock(&single_instance);
278     return VLC_ENOMEM;
279 }
280
281 static void Close(vlc_object_t *p_this)
282 {
283     vout_display_t *vd = (vout_display_t *)p_this;
284     vout_display_sys_t *sys = vd->sys;
285
286     picture_pool_Delete(sys->pool);
287     dlclose(sys->p_library);
288     free(sys);
289     vlc_mutex_unlock(&single_instance);
290 }
291
292 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
293 {
294     VLC_UNUSED(count);
295
296     return vd->sys->pool;
297 }
298
299 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
300 static void SetupPictureYV12( SurfaceInfo* p_surfaceInfo, picture_t *p_picture )
301 {
302     /* according to document of android.graphics.ImageFormat.YV12 */
303     int i_stride = ALIGN_16_PIXELS( p_surfaceInfo->s );
304     int i_c_stride = ALIGN_16_PIXELS( i_stride / 2 );
305
306     p_picture->p->i_pitch = i_stride;
307
308     /* Fill chroma planes for planar YUV */
309     for( int n = 1; n < p_picture->i_planes; n++ )
310     {
311         const plane_t *o = &p_picture->p[n-1];
312         plane_t *p = &p_picture->p[n];
313
314         p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
315         p->i_pitch  = i_c_stride;
316         p->i_lines  = p_picture->format.i_height / 2;
317     }
318
319     if( vlc_fourcc_AreUVPlanesSwapped( p_picture->format.i_chroma,
320                                        VLC_CODEC_YV12 ) ) {
321         uint8_t *p_tmp = p_picture->p[1].p_pixels;
322         p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
323         p_picture->p[2].p_pixels = p_tmp;
324     }
325 }
326
327 static int  AndroidLockSurface(picture_t *picture)
328 {
329     picture_sys_t *picsys = picture->p_sys;
330     vout_display_sys_t *sys = picsys->sys;
331     SurfaceInfo *info;
332     uint32_t sw, sh;
333     void *surf;
334
335     sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
336     sh = picture->p[0].i_visible_lines;
337
338     picsys->surf = surf = jni_LockAndGetAndroidSurface();
339     info = &(picsys->info);
340
341     if (unlikely(!surf)) {
342         jni_UnlockAndroidSurface();
343         return VLC_EGENERIC;
344     }
345
346     if (sys->s_lock)
347         sys->s_lock(surf, info, 1);
348     else
349         sys->s_lock2(surf, info, NULL);
350
351     // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
352     int align_pixels = (16 / picture->p[0].i_pixel_pitch) - 1;
353     uint32_t aligned_width = (sw + align_pixels) & ~align_pixels;
354
355     if (info->w != aligned_width || info->h != sh) {
356         // input size doesn't match the surface size -> request a resize
357         jni_SetAndroidSurfaceSize(sw, sh, sys->i_sar_num, sys->i_sar_den);
358         sys->s_unlockAndPost(surf);
359         jni_UnlockAndroidSurface();
360         return VLC_EGENERIC;
361     }
362
363     picture->p[0].p_pixels = (uint8_t*)info->bits;
364     picture->p[0].i_lines = info->h;
365     picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * info->s;
366
367     if (info->format == 0x32315659 /*ANDROID_IMAGE_FORMAT_YV12*/)
368         SetupPictureYV12(info, picture);
369
370     return VLC_SUCCESS;
371 }
372
373 static void AndroidUnlockSurface(picture_t *picture)
374 {
375     picture_sys_t *picsys = picture->p_sys;
376     vout_display_sys_t *sys = picsys->sys;
377
378     if (likely(picsys->surf))
379         sys->s_unlockAndPost(picsys->surf);
380     jni_UnlockAndroidSurface();
381 }
382
383 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
384 {
385     VLC_UNUSED(vd);
386     VLC_UNUSED(subpicture);
387
388     /* refcount lowers to 0, and pool_cfg.unlock is called */
389
390     picture_Release(picture);
391 }
392
393 static int Control(vout_display_t *vd, int query, va_list args)
394 {
395     VLC_UNUSED(args);
396
397     switch (query) {
398     case VOUT_DISPLAY_HIDE_MOUSE:
399         return VLC_SUCCESS;
400
401     default:
402         msg_Err(vd, "Unknown request in android vout display");
403
404     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
405     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
406     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
407     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
408     case VOUT_DISPLAY_CHANGE_ZOOM:
409     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
410     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
411     case VOUT_DISPLAY_GET_OPENGL:
412         return VLC_EGENERIC;
413     }
414 }