]> git.sesse.net Git - vlc/blob - modules/video_output/androidsurface.c
0ea858fbdf2446f7718790cc537d9cbe3ed2d8bb
[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_UNLOCK
40 # define ANDROID_SYM_S_UNLOCK "_ZN7android7Surface13unlockAndPostEv"
41 #endif
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46
47 static int  Open (vlc_object_t *);
48 static void Close(vlc_object_t *);
49
50 vlc_module_begin()
51     set_category(CAT_VIDEO)
52     set_subcategory(SUBCAT_VIDEO_VOUT)
53     set_shortname("AndroidSurface")
54     set_description(N_("Android Surface video output"))
55     set_capability("vout display", 155)
56     add_shortcut("androidsurface", "android")
57     set_callbacks(Open, Close)
58 vlc_module_end()
59
60 /*****************************************************************************
61  * JNI prototypes
62  *****************************************************************************/
63
64 extern void *jni_LockAndGetAndroidSurface();
65 extern void  jni_UnlockAndroidSurface();
66 extern void  jni_SetAndroidSurfaceSize(int width, int height);
67
68 // _ZN7android7Surface4lockEPNS0_11SurfaceInfoEb
69 typedef void (*Surface_lock)(void *, void *, int);
70 // _ZN7android7Surface13unlockAndPostEv
71 typedef void (*Surface_unlockAndPost)(void *);
72
73 /*****************************************************************************
74  * Local prototypes
75  *****************************************************************************/
76
77 static picture_pool_t   *Pool  (vout_display_t *, unsigned);
78 static void             Display(vout_display_t *, picture_t *, subpicture_t *);
79 static int              Control(vout_display_t *, int, va_list);
80
81 /* */
82 struct vout_display_sys_t {
83     picture_pool_t *pool;
84     void *p_library;
85     Surface_lock s_lock;
86     Surface_unlockAndPost s_unlockAndPost;
87
88     picture_resource_t resource;
89 };
90
91 /* */
92 typedef struct _SurfaceInfo {
93     uint32_t    w;
94     uint32_t    h;
95     uint32_t    s;
96     uint32_t    usage;
97     uint32_t    format;
98     uint32_t*   bits;
99     uint32_t    reserved[2];
100 } SurfaceInfo;
101
102 struct picture_sys_t
103 {
104     void *surf;
105     SurfaceInfo info;
106     vout_display_sys_t *sys;
107 };
108
109 static int  AndroidLockSurface(picture_t *);
110 static void AndroidUnlockSurface(picture_t *);
111
112 static vlc_mutex_t single_instance = VLC_STATIC_MUTEX;
113
114 static inline void *LoadSurface(const char *psz_lib, vout_display_sys_t *sys) {
115     void *p_library = dlopen(psz_lib, RTLD_NOW);
116     if (p_library) {
117         sys->s_lock = (Surface_lock)(dlsym(p_library, ANDROID_SYM_S_LOCK));
118         sys->s_unlockAndPost =
119             (Surface_unlockAndPost)(dlsym(p_library, ANDROID_SYM_S_UNLOCK));
120         if (sys->s_lock && sys->s_unlockAndPost) {
121             return p_library;
122         }
123         dlclose(p_library);
124     }
125     return NULL;
126 }
127
128 static void *InitLibrary(vout_display_sys_t *sys) {
129     void *p_library;
130     if ((p_library = LoadSurface("libsurfaceflinger_client.so", sys)))
131         return p_library;
132     return LoadSurface("libui.so", sys);
133 }
134
135 static int Open(vlc_object_t *p_this) {
136     vout_display_t *vd = (vout_display_t *)p_this;
137     vout_display_sys_t *sys;
138     void *p_library;
139
140     /* */
141     if (vlc_mutex_trylock(&single_instance) != 0) {
142         msg_Err(vd, "Can't start more than one instance at a time");
143         return VLC_EGENERIC;
144     }
145
146     /* Allocate structure */
147     sys = (struct vout_display_sys_t*) calloc(1, sizeof(*sys));
148     if (!sys) {
149         vlc_mutex_unlock(&single_instance);
150         return VLC_ENOMEM;
151     }
152
153     /* */
154     sys->p_library = p_library = InitLibrary(sys);
155     if (!p_library) {
156         free(sys);
157         msg_Err(vd, "Could not initialize libui.so/libsurfaceflinger_client.so!");
158         vlc_mutex_unlock(&single_instance);
159         return VLC_EGENERIC;
160     }
161
162     /* Setup chroma */
163     video_format_t fmt = vd->fmt;
164     fmt.i_chroma = VLC_CODEC_RGB32;
165     fmt.i_rmask  = 0x000000ff;
166     fmt.i_gmask  = 0x0000ff00;
167     fmt.i_bmask  = 0x00ff0000;
168     video_format_FixRgb(&fmt);
169
170     /* Create the associated picture */
171     picture_resource_t *rsc = &sys->resource;
172     rsc->p_sys = malloc(sizeof(*rsc->p_sys));
173     if (!rsc->p_sys)
174         goto enomem;
175     rsc->p_sys->sys = sys;
176
177     for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
178         rsc->p[i].p_pixels = NULL;
179         rsc->p[i].i_pitch = 0;
180         rsc->p[i].i_lines = 0;
181     }
182     picture_t *picture = picture_NewFromResource(&fmt, rsc);
183     if (!picture)
184         goto enomem;
185
186     /* Wrap it into a picture pool */
187     picture_pool_configuration_t pool_cfg;
188     memset(&pool_cfg, 0, sizeof(pool_cfg));
189     pool_cfg.picture_count = 1;
190     pool_cfg.picture       = &picture;
191     pool_cfg.lock          = AndroidLockSurface;
192     pool_cfg.unlock        = AndroidUnlockSurface;
193
194     sys->pool = picture_pool_NewExtended(&pool_cfg);
195     if (!sys->pool) {
196         picture_Release(picture);
197         goto enomem;
198     }
199
200     /* Setup vout_display */
201     vd->sys     = sys;
202     vd->fmt     = fmt;
203     vd->pool    = Pool;
204     vd->display = Display;
205     vd->control = Control;
206     vd->prepare = NULL;
207     vd->manage  = NULL;
208
209     /* Fix initial state */
210     vout_display_SendEventFullscreen(vd, false);
211
212     return VLC_SUCCESS;
213
214 enomem:
215     free(rsc->p_sys);
216     free(sys);
217     dlclose(p_library);
218     vlc_mutex_unlock(&single_instance);
219     return VLC_ENOMEM;
220 }
221
222 static void Close(vlc_object_t *p_this) {
223     vout_display_t *vd = (vout_display_t *)p_this;
224     vout_display_sys_t *sys = vd->sys;
225
226     picture_pool_Delete(sys->pool);
227     dlclose(sys->p_library);
228     free(sys);
229     vlc_mutex_unlock(&single_instance);
230 }
231
232 static picture_pool_t *Pool(vout_display_t *vd, unsigned count) {
233     vout_display_sys_t *sys = vd->sys;
234     VLC_UNUSED(count);
235     return sys->pool;
236 }
237
238 static int  AndroidLockSurface(picture_t *picture) {
239     picture_sys_t *picsys = picture->p_sys;
240     vout_display_sys_t *sys = picsys->sys;
241     SurfaceInfo *info;
242     uint32_t sw, sh;
243     void *surf;
244
245     sw = picture->p[0].i_visible_pitch / picture->p[0].i_pixel_pitch;
246     sh = picture->p[0].i_visible_lines;
247
248     picsys->surf = surf = jni_LockAndGetAndroidSurface();
249     info = &(picsys->info);
250
251     if (unlikely(!surf)) {
252         jni_UnlockAndroidSurface();
253         return VLC_EGENERIC;
254     }
255
256     sys->s_lock(surf, info, 1);
257
258     // input size doesn't match the surface size,
259     // request a resize
260     if (info->w != sw || info->h != sh) {
261         jni_SetAndroidSurfaceSize(sw, sh);
262         sys->s_unlockAndPost(surf);
263         jni_UnlockAndroidSurface();
264         return VLC_EGENERIC;
265     }
266
267     picture->p->p_pixels = (uint8_t*)info->bits;
268     picture->p->i_pitch = 4 * info->s;
269     picture->p->i_lines = info->h;
270
271     return VLC_SUCCESS;
272 }
273
274 static void AndroidUnlockSurface(picture_t *picture) {
275     picture_sys_t *picsys = picture->p_sys;
276     vout_display_sys_t *sys = picsys->sys;
277
278     if (likely(picsys->surf))
279         sys->s_unlockAndPost(picsys->surf);
280     jni_UnlockAndroidSurface();
281 }
282
283 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) {
284     VLC_UNUSED(vd);
285     VLC_UNUSED(subpicture);
286     picture_Release(picture);
287 }
288
289 static int Control(vout_display_t *vd, int query, va_list args) {
290     VLC_UNUSED(args);
291
292     switch (query) {
293         case VOUT_DISPLAY_CHANGE_FULLSCREEN:
294         case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
295         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
296         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
297         case VOUT_DISPLAY_CHANGE_ZOOM:
298         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
299         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
300         case VOUT_DISPLAY_GET_OPENGL:
301             return VLC_EGENERIC;
302         case VOUT_DISPLAY_HIDE_MOUSE:
303             return VLC_SUCCESS;
304         default:
305             msg_Err(vd, "Unknown request in android vout display");
306             return VLC_EGENERIC;
307     }
308 }