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>
35 #include <android/native_window.h>
37 #include <android/native_window_jni.h>
39 #ifndef ANDROID_SYM_S_LOCK
40 # define ANDROID_SYM_S_LOCK "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEb"
42 #ifndef ANDROID_SYM_S_LOCK2
43 # define ANDROID_SYM_S_LOCK2 "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"
45 #ifndef ANDROID_SYM_S_UNLOCK
46 # define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
49 /*****************************************************************************
51 *****************************************************************************/
52 #define CHROMA_TEXT N_("Chroma used")
53 #define CHROMA_LONGTEXT N_(\
54 "Force use of a specific chroma for output. Default is RGB32.")
56 #define CFG_PREFIX "androidsurface-"
58 static int Open (vlc_object_t *);
59 static void Close(vlc_object_t *);
62 set_category(CAT_VIDEO)
63 set_subcategory(SUBCAT_VIDEO_VOUT)
64 set_shortname("AndroidSurface")
65 set_description(N_("Android Surface video output"))
66 set_capability("vout display", 155)
67 add_shortcut("androidsurface", "android")
68 add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
69 set_callbacks(Open, Close)
72 /*****************************************************************************
74 *****************************************************************************/
77 extern void *jni_LockAndGetAndroidSurface();
78 extern jobject jni_LockAndGetAndroidJavaSurface();
79 extern void jni_UnlockAndroidSurface();
80 extern void jni_SetAndroidSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
82 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb
83 typedef void (*Surface_lock)(void *, void *, int);
84 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE
85 typedef void (*Surface_lock2)(void *, void *, void *);
86 // _ZN7android7Surface13unlockAndPostEv
87 typedef void (*Surface_unlockAndPost)(void *);
89 typedef ANativeWindow* (*ptr_ANativeWindow_fromSurface)(JNIEnv*, jobject);
90 typedef void (*ptr_ANativeWindow_release)(ANativeWindow*);
91 typedef int32_t (*ptr_ANativeWindow_lock)(ANativeWindow*, ANativeWindow_Buffer*, ARect*);
92 // Just using the normal Surface_unlockAndPost as prototype for ANativeWindow_unlockAndPost
94 /*****************************************************************************
96 *****************************************************************************/
98 static picture_pool_t *Pool (vout_display_t *, unsigned);
99 static void Display(vout_display_t *, picture_t *, subpicture_t *);
100 static int Control(vout_display_t *, int, va_list);
103 typedef struct _SurfaceInfo {
110 uint32_t reserved[2];
114 struct vout_display_sys_t {
115 picture_pool_t *pool;
118 Surface_lock2 s_lock2;
119 Surface_unlockAndPost s_unlockAndPost;
120 ptr_ANativeWindow_fromSurface s_winFromSurface;
121 ptr_ANativeWindow_release s_winRelease;
122 ptr_ANativeWindow_lock s_winLock;
125 ANativeWindow *window;
135 struct picture_sys_t {
138 vout_display_sys_t *sys;
141 static int AndroidLockSurface(picture_t *);
142 static void AndroidUnlockSurface(picture_t *);
144 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
146 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys)
148 void *p_library = dlopen(psz_lib, RTLD_NOW);
152 sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK));
153 sys->s_lock2 = (Surface_lock2)(dlsym(p_library, ANDROID_SYM_S_LOCK2));
154 sys->s_unlockAndPost =
155 (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK));
157 if ((sys->s_lock || sys->s_lock2) && sys->s_unlockAndPost)
164 static void *InitLibrary(vout_display_sys_t *sys)
166 static const char *libs[] = {
167 "libsurfaceflinger_client.so",
172 for (size_t i = 0; i < sizeof(libs) / sizeof(*libs); i++) {
173 void *lib = LoadSurface(libs[i], sys);
180 static void *InitLibrary2(vout_display_sys_t *sys)
182 void *p_library = dlopen("libandroid.so", RTLD_NOW);
186 sys->s_winFromSurface =
187 (ptr_ANativeWindow_fromSurface)(dlsym(p_library, "ANativeWindow_fromSurface"));
189 (ptr_ANativeWindow_release)(dlsym(p_library, "ANativeWindow_release"));
191 (ptr_ANativeWindow_lock)(dlsym(p_library, "ANativeWindow_lock"));
192 sys->s_unlockAndPost =
193 (Surface_unlockAndPost)(dlsym(p_library, "ANativeWindow_unlockAndPost"));
195 if (sys->s_winFromSurface && sys->s_winRelease && sys->s_winLock && sys->s_unlockAndPost)
198 sys->s_winFromSurface = NULL;
199 sys->s_winRelease = NULL;
200 sys->s_winLock = NULL;
201 sys->s_unlockAndPost = NULL;
207 static int Open(vlc_object_t *p_this)
209 vout_display_t *vd = (vout_display_t *)p_this;
212 if (vlc_mutex_trylock(&single_instance) != 0) {
213 msg_Err(vd, "Can't start more than one instance at a time");
217 /* Allocate structure */
218 vout_display_sys_t *sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
220 vlc_mutex_unlock(&single_instance);
225 sys->p_library = InitLibrary2(sys);
227 sys->p_library = InitLibrary(sys);
228 if (!sys->p_library) {
230 msg_Err(vd, "Could not initialize libandroid.so/libui.so/libgui.so/libsurfaceflinger_client.so!");
231 vlc_mutex_unlock(&single_instance);
236 video_format_t fmt = vd->fmt;
238 char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
240 fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
243 fmt.i_chroma = VLC_CODEC_RGB32;
245 switch(fmt.i_chroma) {
247 /* avoid swscale usage by asking for I420 instead since the
248 * vout already has code to swap the buffers */
249 fmt.i_chroma = VLC_CODEC_I420;
253 case VLC_CODEC_RGB16:
254 fmt.i_bmask = 0x0000001f;
255 fmt.i_gmask = 0x000007e0;
256 fmt.i_rmask = 0x0000f800;
259 case VLC_CODEC_RGB32:
260 fmt.i_rmask = 0x000000ff;
261 fmt.i_gmask = 0x0000ff00;
262 fmt.i_bmask = 0x00ff0000;
268 video_format_FixRgb(&fmt);
270 msg_Dbg(vd, "Pixel format %4.4s", (char*)&fmt.i_chroma);
273 /* Create the associated picture */
274 picture_sys_t *picsys = malloc(sizeof(*picsys));
275 if (unlikely(picsys == NULL))
279 picture_resource_t resource = { .p_sys = picsys };
280 picture_t *picture = picture_NewFromResource(&fmt, &resource);
286 /* Wrap it into a picture pool */
287 picture_pool_configuration_t pool_cfg;
288 memset(&pool_cfg, 0, sizeof(pool_cfg));
289 pool_cfg.picture_count = 1;
290 pool_cfg.picture = &picture;
291 pool_cfg.lock = AndroidLockSurface;
292 pool_cfg.unlock = AndroidUnlockSurface;
294 sys->pool = picture_pool_NewExtended(&pool_cfg);
296 picture_Release(picture);
300 /* Setup vout_display */
304 vd->display = Display;
305 vd->control = Control;
309 /* Fix initial state */
310 vout_display_SendEventFullscreen(vd, false);
312 sys->i_sar_num = vd->source.i_sar_num;
313 sys->i_sar_den = vd->source.i_sar_den;
318 dlclose(sys->p_library);
320 vlc_mutex_unlock(&single_instance);
324 static void Close(vlc_object_t *p_this)
326 vout_display_t *vd = (vout_display_t *)p_this;
327 vout_display_sys_t *sys = vd->sys;
329 picture_pool_Delete(sys->pool);
331 sys->s_winRelease(sys->window);
332 dlclose(sys->p_library);
334 vlc_mutex_unlock(&single_instance);
337 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
341 return vd->sys->pool;
344 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
345 static void SetupPictureYV12( SurfaceInfo* p_surfaceInfo, picture_t *p_picture )
347 /* according to document of android.graphics.ImageFormat.YV12 */
348 int i_stride = ALIGN_16_PIXELS( p_surfaceInfo->s );
349 int i_c_stride = ALIGN_16_PIXELS( i_stride / 2 );
351 p_picture->p->i_pitch = i_stride;
353 /* Fill chroma planes for planar YUV */
354 for( int n = 1; n < p_picture->i_planes; n++ )
356 const plane_t *o = &p_picture->p[n-1];
357 plane_t *p = &p_picture->p[n];
359 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
360 p->i_pitch = i_c_stride;
361 p->i_lines = p_picture->format.i_height / 2;
364 if( vlc_fourcc_AreUVPlanesSwapped( p_picture->format.i_chroma,
366 uint8_t *p_tmp = p_picture->p[1].p_pixels;
367 p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
368 p_picture->p[2].p_pixels = p_tmp;
372 static int AndroidLockSurface(picture_t *picture)
374 picture_sys_t *picsys = picture->p_sys;
375 vout_display_sys_t *sys = picsys->sys;
380 sw = sys->fmt.i_width;
381 sh = sys->fmt.i_height;
383 if (sys->s_winFromSurface) {
384 jobject jsurf = jni_LockAndGetAndroidJavaSurface();
385 if (unlikely(!jsurf)) {
386 jni_UnlockAndroidSurface();
389 if (sys->window && jsurf != sys->jsurf) {
390 sys->s_winRelease(sys->window);
396 (*myVm)->AttachCurrentThread(myVm, &p_env, NULL);
397 sys->window = sys->s_winFromSurface(p_env, jsurf);
398 (*myVm)->DetachCurrentThread(myVm);
400 // Using sys->window instead of the native surface object
401 // as parameter to the unlock function
402 picsys->surf = surf = sys->window;
404 picsys->surf = surf = jni_LockAndGetAndroidSurface();
405 if (unlikely(!surf)) {
406 jni_UnlockAndroidSurface();
410 info = &picsys->info;
412 if (sys->s_winLock) {
413 ANativeWindow_Buffer buf = { 0 };
414 sys->s_winLock(sys->window, &buf, NULL);
416 info->h = buf.height;
417 info->bits = buf.bits;
418 info->s = buf.stride;
419 info->format = buf.format;
420 } else if (sys->s_lock)
421 sys->s_lock(surf, info, 1);
423 sys->s_lock2(surf, info, NULL);
425 // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
426 int align_pixels = (16 / picture->p[0].i_pixel_pitch) - 1;
427 uint32_t aligned_width = (sw + align_pixels) & ~align_pixels;
429 if (info->w != aligned_width || info->h != sh || sys->b_changed_crop) {
430 // input size doesn't match the surface size -> request a resize
431 jni_SetAndroidSurfaceSize(aligned_width, sh, sys->fmt.i_visible_width, sys->fmt.i_visible_height, sys->i_sar_num, sys->i_sar_den);
432 // When using ANativeWindow, one should use ANativeWindow_setBuffersGeometry
433 // to set the size and format. In our case, these are set via the SurfaceHolder
434 // in Java, so we seem to manage without calling this ANativeWindow function.
435 sys->s_unlockAndPost(surf);
436 jni_UnlockAndroidSurface();
437 sys->b_changed_crop = false;
441 picture->p[0].p_pixels = (uint8_t*)info->bits;
442 picture->p[0].i_lines = info->h;
443 picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * info->s;
445 if (info->format == 0x32315659 /*ANDROID_IMAGE_FORMAT_YV12*/)
446 SetupPictureYV12(info, picture);
451 static void AndroidUnlockSurface(picture_t *picture)
453 picture_sys_t *picsys = picture->p_sys;
454 vout_display_sys_t *sys = picsys->sys;
456 if (likely(picsys->surf))
457 sys->s_unlockAndPost(picsys->surf);
458 jni_UnlockAndroidSurface();
461 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
464 VLC_UNUSED(subpicture);
466 /* refcount lowers to 0, and pool_cfg.unlock is called */
468 picture_Release(picture);
471 static int Control(vout_display_t *vd, int query, va_list args)
476 case VOUT_DISPLAY_HIDE_MOUSE:
479 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
483 vout_display_sys_t *sys = vd->sys;
485 const video_format_t *source = (const video_format_t *)va_arg(args, const video_format_t *);
487 sys->b_changed_crop = true;
492 msg_Err(vd, "Unknown request in android vout display");
494 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
495 case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
496 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
497 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
498 case VOUT_DISPLAY_CHANGE_ZOOM:
499 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
500 case VOUT_DISPLAY_GET_OPENGL: