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