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