]> git.sesse.net Git - vlc/blob - modules/video_output/android/android_window.c
android_window: fix deadlock if android surface won't be created
[vlc] / modules / video_output / android / android_window.c
1 /*****************************************************************************
2  * android_window.c: Android video output module
3  *****************************************************************************
4  * Copyright (C) 2014 VLC authors and VideoLAN
5  *
6  * Authors: Thomas Guillem <thomas@gllm.fr>
7  *          Felix Abecassis <felix.abecassis@gmail.com>
8  *          Ming Hu <tewilove@gmail.com>
9  *          Ludovic Fauvet <etix@l0cal.com>
10  *          Sébastien Toque <xilasz@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_vout_display.h>
34 #include <vlc_picture_pool.h>
35 #include <vlc_filter.h>
36 #include <vlc_md5.h>
37
38 #include <dlfcn.h>
39
40 #include "android_window.h"
41 #include "utils.h"
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 #define USE_ANWP
47 #define CHROMA_TEXT N_("Chroma used")
48 #define CHROMA_LONGTEXT N_(\
49     "Force use of a specific chroma for output. Default is RGB32.")
50
51 #define CFG_PREFIX "androidsurface-"
52 static int  Open (vlc_object_t *);
53 static void Close(vlc_object_t *);
54
55 vlc_module_begin()
56     set_category(CAT_VIDEO)
57     set_subcategory(SUBCAT_VIDEO_VOUT)
58     set_shortname("android_window")
59     set_description(N_("Android video output"))
60     set_capability("vout display", 260)
61     add_shortcut("androidwindow", "android")
62     set_callbacks(Open, Close)
63 vlc_module_end()
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68
69 #define THREAD_NAME "android_window"
70 extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
71 extern void jni_detach_thread();
72
73 extern jobject jni_LockAndGetAndroidJavaSurface();
74 extern jobject jni_LockAndGetSubtitlesSurface();
75 extern void  jni_UnlockAndroidSurface();
76
77 extern bool jni_IsVideoPlayerActivityCreated();
78 extern void  jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
79 extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
80 extern int jni_GetWindowSize(int *width, int *height);
81
82 static const vlc_fourcc_t subpicture_chromas[] =
83 {
84     VLC_CODEC_RGBA,
85     0
86 };
87
88 static picture_pool_t   *Pool  (vout_display_t *, unsigned);
89 static void             Prepare(vout_display_t *, picture_t *, subpicture_t *);
90 static void             Display(vout_display_t *, picture_t *, subpicture_t *);
91 static int              Control(vout_display_t *, int, va_list);
92
93 typedef struct android_window android_window;
94 struct android_window
95 {
96     video_format_t fmt;
97     int i_android_hal;
98     unsigned int i_angle;
99     unsigned int i_pic_count;
100     unsigned int i_min_undequeued;
101     bool b_use_priv;
102     bool b_opaque;
103
104     jobject jsurf;
105     ANativeWindow *p_handle;
106     native_window_priv *p_handle_priv;
107 };
108
109 typedef struct buffer_bounds buffer_bounds;
110 struct buffer_bounds
111 {
112     uint8_t *p_pixels;
113     ARect bounds;
114 };
115
116 struct vout_display_sys_t
117 {
118     picture_pool_t *pool;
119
120     int i_display_width;
121     int i_display_height;
122
123     void *p_library;
124     native_window_api_t anw;
125     native_window_priv_api_t anwp;
126     bool b_has_anwp;
127
128     android_window *p_window;
129     android_window *p_sub_window;
130
131     bool b_sub_invalid;
132     filter_t *p_spu_blend;
133     picture_t *p_sub_pic;
134     buffer_bounds *p_sub_buffer_bounds;
135     bool b_sub_pic_locked;
136
137     bool b_has_subpictures;
138
139     uint8_t hash[16];
140 };
141
142 static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
143 {
144     unsigned int i_width, i_height;
145     unsigned int i_sar_num = 1, i_sar_den = 1;
146     video_format_t rot_fmt;
147
148     video_format_ApplyRotation(&rot_fmt, p_fmt);
149
150     if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
151         i_sar_num = rot_fmt.i_sar_num;
152         i_sar_den = rot_fmt.i_sar_den;
153     }
154     if (b_cropped) {
155         i_width = rot_fmt.i_visible_width;
156         i_height = rot_fmt.i_visible_height;
157     } else {
158         i_width = rot_fmt.i_width;
159         i_height = rot_fmt.i_height;
160     }
161
162     jni_SetSurfaceLayout(i_width, i_height,
163                          rot_fmt.i_visible_width,
164                          rot_fmt.i_visible_height,
165                          i_sar_num,
166                          i_sar_den);
167     return 0;
168 }
169
170 static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt)
171 {
172     picture_t *p_pic;
173     picture_resource_t rsc;
174     picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
175
176     if (unlikely(p_picsys == NULL))
177         return NULL;
178
179     p_picsys->p_vd_sys = sys;
180
181     memset(&rsc, 0, sizeof(picture_resource_t));
182     rsc.p_sys = p_picsys,
183
184     p_pic = picture_NewFromResource(fmt, &rsc);
185     if (!p_pic)
186     {
187         free(p_picsys);
188         return NULL;
189     }
190     return p_pic;
191 }
192
193 static void FixSubtitleFormat(vout_display_sys_t *sys)
194 {
195     video_format_t *p_subfmt = &sys->p_sub_window->fmt;
196     video_format_t fmt;
197     int i_width, i_height;
198     int i_display_width, i_display_height;
199     double aspect;
200
201     video_format_ApplyRotation(&fmt, &sys->p_window->fmt);
202
203     if (fmt.i_visible_width == 0 || fmt.i_visible_height == 0) {
204         i_width = fmt.i_width;
205         i_height = fmt.i_height;
206     } else {
207         i_width = fmt.i_visible_width;
208         i_height = fmt.i_visible_height;
209     }
210
211     if (fmt.i_sar_num > 0 && fmt.i_sar_den > 0) {
212         if (fmt.i_sar_num >= fmt.i_sar_den)
213             i_width = i_width * fmt.i_sar_num / fmt.i_sar_den;
214         else
215             i_height = i_height * fmt.i_sar_den / fmt.i_sar_num;
216     }
217
218     if (sys->p_window->i_angle == 90 || sys->p_window->i_angle == 180) {
219         i_display_width = sys->i_display_height;
220         i_display_height = sys->i_display_width;
221         aspect = i_height / (double) i_width;
222     } else {
223         i_display_width = sys->i_display_width;
224         i_display_height = sys->i_display_height;
225         aspect = i_width / (double) i_height;
226     }
227
228     if (i_display_width / aspect < i_display_height) {
229         i_width = i_display_width;
230         i_height = i_display_width / aspect;
231     } else {
232         i_width = i_display_height * aspect;
233         i_height = i_display_height;
234     }
235
236     p_subfmt->i_width =
237     p_subfmt->i_visible_width = i_width;
238     p_subfmt->i_height =
239     p_subfmt->i_visible_height = i_height;
240     p_subfmt->i_x_offset = 0;
241     p_subfmt->i_y_offset = 0;
242     p_subfmt->i_sar_num = 1;
243     p_subfmt->i_sar_den = 1;
244     sys->b_sub_invalid = true;
245 }
246
247 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
248 static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
249 {
250     /* according to document of android.graphics.ImageFormat.YV12 */
251     int i_stride = ALIGN_16_PIXELS(i_in_stride);
252     int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
253
254     p_picture->p->i_pitch = i_stride;
255
256     /* Fill chroma planes for planar YUV */
257     for (int n = 1; n < p_picture->i_planes; n++)
258     {
259         const plane_t *o = &p_picture->p[n-1];
260         plane_t *p = &p_picture->p[n];
261
262         p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
263         p->i_pitch  = i_c_stride;
264         p->i_lines  = p_picture->format.i_height / 2;
265         /*
266           Explicitly set the padding lines of the picture to black (127 for YUV)
267           since they might be used by Android during rescaling.
268         */
269         int visible_lines = p_picture->format.i_visible_height / 2;
270         if (visible_lines < p->i_lines)
271             memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
272     }
273
274     if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
275                                       VLC_CODEC_YV12)) {
276         uint8_t *p_tmp = p_picture->p[1].p_pixels;
277         p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
278         p_picture->p[2].p_pixels = p_tmp;
279     }
280 }
281
282 static android_window *AndroidWindow_New(vout_display_sys_t *sys,
283                                          video_format_t *p_fmt,
284                                          bool b_use_priv)
285 {
286     android_window *p_window = calloc(1, sizeof(android_window));
287
288     if (!p_window)
289         return NULL;
290
291     p_window->b_opaque = p_fmt->i_chroma == VLC_CODEC_ANDROID_OPAQUE;
292     if (!p_window->b_opaque) {
293         p_window->b_use_priv = sys->b_has_anwp && b_use_priv;
294
295         p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
296         if (p_window->i_android_hal == -1) {
297             free(p_window);
298             return NULL;
299         }
300     }
301
302     switch (p_fmt->orientation)
303     {
304         case ORIENT_ROTATED_90:
305             p_window->i_angle = 90;
306             break;
307         case ORIENT_ROTATED_180:
308             p_window->i_angle = 180;
309             break;
310         case ORIENT_ROTATED_270:
311             p_window->i_angle = 270;
312             break;
313         default:
314             p_window->i_angle = 0;
315     }
316     if (p_window->b_use_priv)
317         p_window->fmt = *p_fmt;
318     else
319         video_format_ApplyRotation(&p_window->fmt, p_fmt);
320     p_window->i_pic_count = 1;
321     return p_window;
322 }
323
324 static void AndroidWindow_Destroy(vout_display_sys_t *sys,
325                                   android_window *p_window)
326 {
327     if (p_window->p_handle_priv)
328         sys->anwp.disconnect(p_window->p_handle_priv);
329     if (p_window->p_handle)
330         sys->anw.winRelease(p_window->p_handle);
331     free(p_window);
332 }
333
334 static int AndroidWindow_UpdateCrop(vout_display_sys_t *sys,
335                                     android_window *p_window)
336 {
337     if (!p_window->p_handle_priv)
338         return -1;
339
340     return sys->anwp.setCrop(p_window->p_handle_priv,
341                              p_window->fmt.i_x_offset,
342                              p_window->fmt.i_y_offset,
343                              p_window->fmt.i_visible_width,
344                              p_window->fmt.i_visible_height);
345 }
346
347 static int AndroidWindow_SetSurface(vout_display_sys_t *sys,
348                                     android_window *p_window,
349                                     jobject jsurf)
350 {
351     if (p_window->p_handle && jsurf != p_window->jsurf) {
352         if (p_window->p_handle_priv) {
353             sys->anwp.disconnect(p_window->p_handle_priv);
354             p_window->p_handle_priv = NULL;
355         }
356         sys->anw.winRelease(p_window->p_handle);
357         p_window->p_handle = NULL;
358     }
359
360     p_window->jsurf = jsurf;
361     if (!p_window->p_handle && !p_window->b_opaque) {
362         JNIEnv *p_env;
363
364         jni_attach_thread(&p_env, THREAD_NAME);
365         if (!p_env)
366             return -1;
367         p_window->p_handle = sys->anw.winFromSurface(p_env, p_window->jsurf);
368         jni_detach_thread();
369         if (!p_window->p_handle)
370             return -1;
371     }
372
373     return 0;
374 }
375
376 static int AndroidWindow_SetupANWP(vout_display_sys_t *sys,
377                                    android_window *p_window)
378 {
379     unsigned int i_max_buffer_count = 0;
380
381     if (!p_window->p_handle_priv)
382         p_window->p_handle_priv = sys->anwp.connect(p_window->p_handle);
383
384     if (!p_window->p_handle_priv)
385         goto error;
386
387     if (sys->anwp.setup(p_window->p_handle_priv,
388                         p_window->fmt.i_width, p_window->fmt.i_height,
389                         p_window->i_android_hal,
390                         false, 0) != 0)
391         goto error;
392
393     sys->anwp.getMinUndequeued(p_window->p_handle_priv,
394                                &p_window->i_min_undequeued);
395
396     sys->anwp.getMaxBufferCount(p_window->p_handle_priv, &i_max_buffer_count);
397
398     if ((p_window->i_min_undequeued + p_window->i_pic_count) >
399          i_max_buffer_count)
400         p_window->i_pic_count = i_max_buffer_count - p_window->i_min_undequeued;
401
402     if (sys->anwp.setBufferCount(p_window->p_handle_priv,
403                                  p_window->i_pic_count +
404                                  p_window->i_min_undequeued) != 0)
405         goto error;
406
407     if (sys->anwp.setOrientation(p_window->p_handle_priv,
408                                  p_window->i_angle) != 0)
409         goto error;
410
411     AndroidWindow_UpdateCrop(sys, p_window);
412
413     return 0;
414 error:
415     if (p_window->p_handle_priv) {
416         sys->anwp.disconnect(p_window->p_handle_priv);
417         p_window->p_handle_priv = NULL;
418     }
419     p_window->b_use_priv = false;
420     if (p_window->i_angle != 0)
421         video_format_ApplyRotation(&p_window->fmt, &p_window->fmt);
422     return -1;
423 }
424
425 static int AndroidWindow_ConfigureSurface(vout_display_sys_t *sys,
426                                           android_window *p_window)
427 {
428     int err;
429     bool configured;
430
431     /*
432      * anw.setBuffersGeometry and anwp.setup are broken before ics.
433      * use jni_ConfigureSurface to configure the surface on the java side
434      * synchronously.
435      * jni_ConfigureSurface return -1 when you don't need to call it (ie, after
436      * honeycomb).
437      * if jni_ConfigureSurface succeed, you need to get a new surface handle.
438      * That's why AndroidWindow_SetSurface is called again here.
439      */
440     err = jni_ConfigureSurface(p_window->jsurf,
441                                p_window->fmt.i_width,
442                                p_window->fmt.i_height,
443                                p_window->i_android_hal,
444                                &configured);
445     if (err == 0) {
446         if (configured) {
447             jobject jsurf = p_window->jsurf;
448             p_window->jsurf = NULL;
449             if (AndroidWindow_SetSurface(sys, p_window, jsurf) != 0)
450                 return -1;
451         } else
452             return -1;
453     }
454     return 0;
455 }
456
457 static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
458                                   android_window *p_window)
459 {
460     p_window->i_pic_count = 1;
461     p_window->i_min_undequeued = 0;
462
463     return sys->anw.setBuffersGeometry(p_window->p_handle,
464                                        p_window->fmt.i_width,
465                                        p_window->fmt.i_height,
466                                        p_window->i_android_hal);
467 }
468
469 static int AndroidWindow_Setup(vout_display_sys_t *sys,
470                                     android_window *p_window,
471                                     unsigned int i_pic_count)
472 {
473     if (i_pic_count != 0)
474         p_window->i_pic_count = i_pic_count;
475
476     if (!p_window->b_opaque) {
477         int align_pixels;
478         picture_t *p_pic = PictureAlloc(sys, &p_window->fmt);
479
480         // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
481         align_pixels = (16 / p_pic->p[0].i_pixel_pitch) - 1;
482         p_window->fmt.i_height = p_pic->format.i_height;
483         p_window->fmt.i_width = (p_pic->format.i_width + align_pixels) & ~align_pixels;
484         picture_Release(p_pic);
485     }
486
487     if (AndroidWindow_ConfigureSurface(sys, p_window) != 0)
488         return -1;
489
490     if (p_window->b_opaque) {
491         sys->p_window->i_pic_count = 31; // TODO
492         sys->p_window->i_min_undequeued = 0;
493         return 0;
494     }
495
496     if (!p_window->b_use_priv
497         || AndroidWindow_SetupANWP(sys, p_window) != 0) {
498         if (AndroidWindow_SetupANW(sys, p_window) != 0)
499             return -1;
500     }
501
502     return 0;
503 }
504
505 static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
506                                         android_window *p_window,
507                                         picture_t *p_pic,
508                                         bool b_render)
509 {
510     picture_sys_t *p_picsys = p_pic->p_sys;
511
512     if (p_window->b_use_priv) {
513         void *p_handle = p_picsys->priv.sw.p_handle;
514
515         if (p_handle == NULL)
516             return;
517
518         sys->anwp.unlockData(p_window->p_handle_priv, p_handle, b_render);
519     } else
520         sys->anw.unlockAndPost(p_window->p_handle);
521 }
522
523 static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
524                                      android_window *p_window,
525                                      picture_t *p_pic)
526 {
527     picture_sys_t *p_picsys = p_pic->p_sys;
528
529     if (p_window->b_use_priv) {
530         void *p_handle;
531         int err;
532
533         err = sys->anwp.lockData(p_window->p_handle_priv,
534                                  &p_handle,
535                                  &p_picsys->priv.sw.buf);
536         if (err != 0)
537             return -1;
538         p_picsys->priv.sw.p_handle = p_handle;
539     } else {
540         if (sys->anw.winLock(p_window->p_handle,
541                              &p_picsys->priv.sw.buf, NULL) != 0)
542             return -1;
543     }
544     if (p_picsys->priv.sw.buf.width < 0 ||
545         p_picsys->priv.sw.buf.height < 0 ||
546         (unsigned)p_picsys->priv.sw.buf.width < p_window->fmt.i_width ||
547         (unsigned)p_picsys->priv.sw.buf.height < p_window->fmt.i_height) {
548         AndroidWindow_UnlockPicture(sys, p_window, p_pic, false);
549         return -1;
550     }
551
552     p_pic->p[0].p_pixels = p_picsys->priv.sw.buf.bits;
553     p_pic->p[0].i_lines = p_picsys->priv.sw.buf.height;
554     p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->priv.sw.buf.stride;
555
556     if (p_picsys->priv.sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
557         SetupPictureYV12(p_pic, p_picsys->priv.sw.buf.stride);
558
559     return 0;
560 }
561
562 static int SetupWindowSurface(vout_display_sys_t *sys, unsigned i_pic_count)
563 {
564     int err;
565     jobject jsurf = jni_LockAndGetAndroidJavaSurface();
566     err = AndroidWindow_SetSurface(sys, sys->p_window, jsurf);
567     jni_UnlockAndroidSurface();
568     err = err == 0 ? AndroidWindow_Setup(sys, sys->p_window, i_pic_count) : err;
569     return err;
570 }
571
572 static int SetupWindowSubtitleSurface(vout_display_sys_t *sys)
573 {
574     int err;
575     jobject jsurf = jni_LockAndGetSubtitlesSurface();
576     err = AndroidWindow_SetSurface(sys, sys->p_sub_window, jsurf);
577     jni_UnlockAndroidSurface();
578     err = err == 0 ? AndroidWindow_Setup(sys, sys->p_sub_window, 1) : err;
579     return err;
580 }
581
582 static void SetRGBMask(video_format_t *p_fmt)
583 {
584     switch(p_fmt->i_chroma) {
585         case VLC_CODEC_RGB16:
586             p_fmt->i_bmask = 0x0000001f;
587             p_fmt->i_gmask = 0x000007e0;
588             p_fmt->i_rmask = 0x0000f800;
589             break;
590
591         case VLC_CODEC_RGB32:
592         case VLC_CODEC_RGBA:
593             p_fmt->i_rmask = 0x000000ff;
594             p_fmt->i_gmask = 0x0000ff00;
595             p_fmt->i_bmask = 0x00ff0000;
596             break;
597     }
598 }
599
600 static void SendEventDisplaySize(vout_display_t *vd)
601 {
602     vout_display_sys_t *sys = vd->sys;
603     int i_display_width, i_display_height;
604
605     if (jni_GetWindowSize(&i_display_width, &i_display_height) == 0
606         && i_display_width != 0 && i_display_height != 0
607         && (i_display_width != sys->i_display_width
608          || i_display_height != sys->i_display_height))
609         vout_display_SendEventDisplaySize(vd, i_display_width,
610                                               i_display_height);
611 }
612
613 static int Open(vlc_object_t *p_this)
614 {
615     vout_display_t *vd = (vout_display_t*)p_this;
616     vout_display_sys_t *sys;
617     video_format_t sub_fmt;
618
619     if (vout_display_IsWindowed(vd))
620         return VLC_EGENERIC;
621
622     /* XXX: android_window use a surface created by VideoPlayerActivity to
623      * alloc pictures. Don't try to open the vout if this activity is not
624      * created. This need to be replaced by something like var_CreateGetAddress
625      * (vd, "drawable-android") in the future. */
626     if (!jni_IsVideoPlayerActivityCreated())
627         return VLC_EGENERIC;
628
629     /* Allocate structure */
630     vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
631     if (!sys)
632         return VLC_ENOMEM;
633
634     sys->p_library = LoadNativeWindowAPI(&sys->anw);
635     if (!sys->p_library) {
636         msg_Err(vd, "Could not initialize NativeWindow API.");
637         goto error;
638     }
639
640 #ifdef USE_ANWP
641     if (LoadNativeWindowPrivAPI(&sys->anwp) == 0)
642         sys->b_has_anwp = true;
643     else
644         msg_Warn(vd, "Could not initialize NativeWindow Priv API.");
645 #endif
646
647     sys->i_display_width = vd->cfg->display.width;
648     sys->i_display_height = vd->cfg->display.height;
649
650     if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
651         /* Setup chroma */
652         char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
653         if (psz_fcc) {
654             vd->fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
655             free(psz_fcc);
656         } else
657             vd->fmt.i_chroma = VLC_CODEC_RGB32;
658
659         switch(vd->fmt.i_chroma) {
660             case VLC_CODEC_YV12:
661                 /* avoid swscale usage by asking for I420 instead since the
662                  * vout already has code to swap the buffers */
663                 vd->fmt.i_chroma = VLC_CODEC_I420;
664             case VLC_CODEC_I420:
665                 break;
666             case VLC_CODEC_RGB16:
667             case VLC_CODEC_RGB32:
668             case VLC_CODEC_RGBA:
669                 SetRGBMask(&vd->fmt);
670                 video_format_FixRgb(&vd->fmt);
671                 break;
672             default:
673                 goto error;
674         }
675     }
676
677     sys->p_window = AndroidWindow_New(sys, &vd->fmt, true);
678     if (!sys->p_window)
679         goto error;
680
681     if (SetupWindowSurface(sys, 0) != 0)
682         goto error;
683
684     /* use software rotation if we don't use private anw */
685     if (!sys->p_window->b_opaque && !sys->p_window->b_use_priv)
686         video_format_ApplyRotation(&vd->fmt, &vd->fmt);
687
688     msg_Dbg(vd, "using %s", sys->p_window->b_opaque ? "opaque" :
689             (sys->p_window->b_use_priv ? "ANWP" : "ANW"));
690
691     video_format_ApplyRotation(&sub_fmt, &vd->fmt);
692     sub_fmt.i_chroma = subpicture_chromas[0];
693     SetRGBMask(&sub_fmt);
694     video_format_FixRgb(&sub_fmt);
695     sys->p_sub_window = AndroidWindow_New(sys, &sub_fmt, false);
696     if (!sys->p_sub_window)
697         goto error;
698     FixSubtitleFormat(sys);
699
700     /* Export the subpicture capability of this vout. */
701     vd->info.subpicture_chromas = subpicture_chromas;
702
703     /* Setup vout_display */
704     vd->pool    = Pool;
705     vd->prepare = Prepare;
706     vd->display = Display;
707     vd->control = Control;
708     vd->manage  = Manage;
709
710     /* Fix initial state */
711     vout_display_SendEventFullscreen(vd, true);
712     SendEventDisplaySize(vd);
713
714     return VLC_SUCCESS;
715
716 error:
717     Close(p_this);
718     return VLC_ENOMEM;
719 }
720
721 static void Close(vlc_object_t *p_this)
722 {
723     vout_display_t *vd = (vout_display_t *)p_this;
724     vout_display_sys_t *sys = vd->sys;
725
726     if (!sys)
727         return;
728
729     if (sys->pool)
730         picture_pool_Release(sys->pool);
731     if (sys->p_window)
732         AndroidWindow_Destroy(sys, sys->p_window);
733
734     if (sys->p_sub_pic)
735         picture_Release(sys->p_sub_pic);
736     if (sys->p_spu_blend)
737         filter_DeleteBlend(sys->p_spu_blend);
738     free(sys->p_sub_buffer_bounds);
739     if (sys->p_sub_window)
740         AndroidWindow_Destroy(sys, sys->p_sub_window);
741
742     if (sys->p_library)
743         dlclose(sys->p_library);
744
745     free(sys);
746 }
747
748 static int DefaultLockPicture(picture_t *p_pic)
749 {
750     picture_sys_t *p_picsys = p_pic->p_sys;
751     vout_display_sys_t *sys = p_picsys->p_vd_sys;
752
753     return AndroidWindow_LockPicture(sys, sys->p_window, p_pic);
754 }
755
756 static void DefaultUnlockPicture(picture_t *p_pic, bool b_render)
757 {
758     picture_sys_t *p_picsys = p_pic->p_sys;
759     vout_display_sys_t *sys = p_picsys->p_vd_sys;
760
761     AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic, b_render);
762 }
763
764 static void UnlockPicture(picture_t *p_pic, bool b_render)
765 {
766     picture_sys_t *p_picsys = p_pic->p_sys;
767
768     if (p_picsys->b_locked && p_picsys->pf_unlock_pic)
769         p_picsys->pf_unlock_pic(p_pic, b_render);
770     p_picsys->b_locked  = false;
771 }
772
773 static int PoolLockPicture(picture_t *p_pic)
774 {
775     picture_sys_t *p_picsys = p_pic->p_sys;
776
777     if (p_picsys->pf_lock_pic && p_picsys->pf_lock_pic(p_pic) != 0)
778         return -1;
779     p_picsys->b_locked = true;
780     return 0;
781 }
782
783 static void PoolUnlockPicture(picture_t *p_pic)
784 {
785     UnlockPicture(p_pic, false);
786 }
787
788 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
789 {
790     vout_display_sys_t *sys = vd->sys;
791     picture_pool_t *pool = NULL;
792     picture_t **pp_pics = NULL;
793     unsigned int i = 0;
794
795     msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
796     if (SetupWindowSurface(sys, requested_count) != 0)
797         goto error;
798
799     requested_count = sys->p_window->i_pic_count;
800     msg_Dbg(vd, "PoolAlloc: got %d frames", requested_count);
801
802     UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
803
804     pp_pics = calloc(requested_count, sizeof(picture_t));
805
806     for (i = 0; i < requested_count; i++)
807     {
808         picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt);
809         if (!p_pic)
810             goto error;
811         if (!sys->p_window->b_opaque) {
812             p_pic->p_sys->pf_lock_pic = DefaultLockPicture;
813             p_pic->p_sys->pf_unlock_pic = DefaultUnlockPicture;
814         }
815
816         pp_pics[i] = p_pic;
817     }
818
819     picture_pool_configuration_t pool_cfg;
820     memset(&pool_cfg, 0, sizeof(pool_cfg));
821     pool_cfg.picture_count = requested_count;
822     pool_cfg.picture       = pp_pics;
823     pool_cfg.lock          = PoolLockPicture;
824     pool_cfg.unlock        = PoolUnlockPicture;
825     pool = picture_pool_NewExtended(&pool_cfg);
826
827 error:
828     if (!pool && pp_pics) {
829         for (unsigned j = 0; j < i; j++)
830             picture_Release(pp_pics[j]);
831     }
832     free(pp_pics);
833     return pool;
834 }
835
836 static void SubtitleRegionToBounds(subpicture_t *subpicture,
837                                    ARect *p_out_bounds)
838 {
839     if (subpicture) {
840         for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
841             ARect new_bounds;
842
843             new_bounds.left = r->i_x;
844             new_bounds.top = r->i_y;
845             new_bounds.right = r->fmt.i_visible_width + r->i_x;
846             new_bounds.bottom = r->fmt.i_visible_height + r->i_y;
847             if (r == &subpicture->p_region[0])
848                 *p_out_bounds = new_bounds;
849             else {
850                 if (p_out_bounds->left > new_bounds.left)
851                     p_out_bounds->left = new_bounds.left;
852                 if (p_out_bounds->right < new_bounds.right)
853                     p_out_bounds->right = new_bounds.right;
854                 if (p_out_bounds->top > new_bounds.top)
855                     p_out_bounds->top = new_bounds.top;
856                 if (p_out_bounds->bottom < new_bounds.bottom)
857                     p_out_bounds->bottom = new_bounds.bottom;
858             }
859         }
860     } else {
861         p_out_bounds->left = p_out_bounds->top = 0;
862         p_out_bounds->right = p_out_bounds->bottom = 0;
863     }
864 }
865
866 static void SubtitleGetDirtyBounds(vout_display_t *vd,
867                                    subpicture_t *subpicture,
868                                    ARect *p_out_bounds)
869 {
870     vout_display_sys_t *sys = vd->sys;
871     int i = 0;
872     bool b_found = false;
873
874     /* Try to find last bounds set by current locked buffer.
875      * Indeed, even if we can lock only one buffer at a time, differents
876      * buffers can be locked. This functions will find the last bounds set by
877      * the current buffer. */
878     if (sys->p_sub_buffer_bounds) {
879         for (; sys->p_sub_buffer_bounds[i].p_pixels != NULL; ++i) {
880             buffer_bounds *p_bb = &sys->p_sub_buffer_bounds[i];
881             if (p_bb->p_pixels == sys->p_sub_pic->p[0].p_pixels) {
882                 *p_out_bounds = p_bb->bounds;
883                 b_found = true;
884                 break;
885             }
886         }
887     }
888
889     if (!b_found
890      || p_out_bounds->left < 0
891      || p_out_bounds->right < 0
892      || (unsigned int) p_out_bounds->right > sys->p_sub_pic->format.i_width
893      || p_out_bounds->bottom < 0
894      || p_out_bounds->top < 0
895      || (unsigned int) p_out_bounds->top > sys->p_sub_pic->format.i_height)
896     {
897         /* default is full picture */
898         p_out_bounds->left = 0;
899         p_out_bounds->top = 0;
900         p_out_bounds->right = sys->p_sub_pic->format.i_width;
901         p_out_bounds->bottom = sys->p_sub_pic->format.i_height;
902     }
903
904     /* buffer not found, add it to the array */
905     if (!sys->p_sub_buffer_bounds
906      || sys->p_sub_buffer_bounds[i].p_pixels == NULL) {
907         buffer_bounds *p_bb = realloc(sys->p_sub_buffer_bounds,
908                                       (i + 2) * sizeof(buffer_bounds)); 
909         if (p_bb) {
910             sys->p_sub_buffer_bounds = p_bb;
911             sys->p_sub_buffer_bounds[i].p_pixels = sys->p_sub_pic->p[0].p_pixels;
912             sys->p_sub_buffer_bounds[i+1].p_pixels = NULL;
913         }
914     }
915
916     /* set buffer bounds */
917     if (sys->p_sub_buffer_bounds
918      && sys->p_sub_buffer_bounds[i].p_pixels != NULL)
919         SubtitleRegionToBounds(subpicture, &sys->p_sub_buffer_bounds[i].bounds);
920 }
921
922 static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture)
923 {
924     vout_display_sys_t *sys = vd->sys;
925     struct md5_s hash;
926     ARect memset_bounds;
927
928     InitMD5(&hash);
929     if (subpicture) {
930         for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
931             AddMD5(&hash, &r->i_x, sizeof(r->i_x));
932             AddMD5(&hash, &r->i_y, sizeof(r->i_y));
933             AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
934             AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
935             AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
936             AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
937             const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
938                                       r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
939
940             for (unsigned int y = 0; y < r->fmt.i_visible_height; y++)
941                 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
942         }
943     }
944     EndMD5(&hash);
945     if (!memcmp(hash.buf, sys->hash, 16))
946         return;
947     memcpy(sys->hash, hash.buf, 16);
948
949     if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
950         return;
951
952     sys->b_sub_pic_locked = true;
953
954     /* Clear the subtitles surface. */
955     SubtitleGetDirtyBounds(vd, subpicture, &memset_bounds);
956     const int x_pixels_offset = memset_bounds.left
957                                 * sys->p_sub_pic->p[0].i_pixel_pitch;
958     const int i_line_size = (memset_bounds.right - memset_bounds.left)
959                             * sys->p_sub_pic->p->i_pixel_pitch;
960     for (int y = memset_bounds.top; y < memset_bounds.bottom; y++)
961         memset(&sys->p_sub_pic->p[0].p_pixels[y * sys->p_sub_pic->p[0].i_pitch
962                                               + x_pixels_offset], 0, i_line_size);
963
964     if (subpicture)
965         picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
966 }
967
968 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
969 {
970     vout_display_sys_t *sys = vd->sys;
971
972     if (sys->pool == NULL)
973         sys->pool = PoolAlloc(vd, requested_count);
974     return sys->pool;
975 }
976
977 static void Prepare(vout_display_t *vd, picture_t *picture,
978                     subpicture_t *subpicture)
979 {
980     vout_display_sys_t *sys = vd->sys;
981     VLC_UNUSED(picture);
982
983     SendEventDisplaySize(vd);
984
985     if (subpicture) {
986         if (sys->b_sub_invalid) {
987             sys->b_sub_invalid = false;
988             if (sys->p_sub_pic) {
989                 picture_Release(sys->p_sub_pic);
990                 sys->p_sub_pic = NULL;
991             }
992             if (sys->p_spu_blend) {
993                 filter_DeleteBlend(sys->p_spu_blend);
994                 sys->p_spu_blend = NULL;
995             }
996             free(sys->p_sub_buffer_bounds);
997             sys->p_sub_buffer_bounds = NULL;
998         }
999
1000         if (!sys->p_sub_pic && SetupWindowSubtitleSurface(sys) == 0)
1001             sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt);
1002         if (!sys->p_spu_blend)
1003             sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
1004                                                &sys->p_sub_pic->format);
1005
1006         if (sys->p_sub_pic && sys->p_spu_blend)
1007             sys->b_has_subpictures = true;
1008     }
1009     /* As long as no subpicture was received, do not call
1010        SubpictureDisplay since JNI calls and clearing the subtitles
1011        surface are expensive operations. */
1012     if (sys->b_has_subpictures)
1013     {
1014         SubpicturePrepare(vd, subpicture);
1015         if (!subpicture)
1016         {
1017             /* The surface has been cleared and there is no new
1018                subpicture to upload, do not clear again until a new
1019                subpicture is received. */
1020             sys->b_has_subpictures = false;
1021         }
1022     }
1023 }
1024
1025 static void Display(vout_display_t *vd, picture_t *picture,
1026                     subpicture_t *subpicture)
1027 {
1028     vout_display_sys_t *sys = vd->sys;
1029
1030     /* refcount lowers to 0, and pool_cfg.unlock is called */
1031     UnlockPicture(picture, true);
1032     picture_Release(picture);
1033
1034     if (sys->b_sub_pic_locked) {
1035         sys->b_sub_pic_locked = false;
1036         AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic, true);
1037     }
1038     if (subpicture)
1039         subpicture_Delete(subpicture);
1040 }
1041
1042 static void CopySourceAspect(video_format_t *p_dest,
1043                              const video_format_t *p_src)
1044 {
1045     p_dest->i_sar_num = p_src->i_sar_num;
1046     p_dest->i_sar_den = p_src->i_sar_den;
1047 }
1048
1049 static int Control(vout_display_t *vd, int query, va_list args)
1050 {
1051     vout_display_sys_t *sys = vd->sys;
1052
1053     switch (query) {
1054     case VOUT_DISPLAY_HIDE_MOUSE:
1055     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
1056         return VLC_SUCCESS;
1057     case VOUT_DISPLAY_RESET_PICTURES:
1058     {
1059         if (sys->p_window->b_opaque)
1060             return VLC_EGENERIC;
1061
1062         msg_Dbg(vd, "resetting pictures");
1063
1064         if (sys->pool != NULL)
1065         {
1066             picture_pool_Release(sys->pool);
1067             sys->pool = NULL;
1068         }
1069         return VLC_SUCCESS;
1070     }
1071     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
1072     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
1073     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
1074     {
1075         if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
1076          || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1077             const video_format_t *source;
1078
1079             msg_Dbg(vd, "change source crop/aspect");
1080             source = va_arg(args, const video_format_t *);
1081
1082             if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1083                 video_format_CopyCrop(&sys->p_window->fmt, source);
1084                 AndroidWindow_UpdateCrop(sys, sys->p_window);
1085             } else
1086                 CopySourceAspect(&sys->p_window->fmt, source);
1087
1088             UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
1089         } else {
1090             const vout_display_cfg_t *cfg;
1091
1092             cfg = va_arg(args, const vout_display_cfg_t *);
1093
1094             sys->i_display_width = cfg->display.width;
1095             sys->i_display_height = cfg->display.height;
1096             msg_Dbg(vd, "change display size: %dx%d", sys->i_display_width,
1097                                                       sys->i_display_height);
1098         }
1099         FixSubtitleFormat(sys);
1100
1101         return VLC_SUCCESS;
1102     }
1103     default:
1104         msg_Warn(vd, "Unknown request in android_window");
1105     case VOUT_DISPLAY_CHANGE_ZOOM:
1106     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
1107         return VLC_EGENERIC;
1108     }
1109 }