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