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