1 /*****************************************************************************
2 * android_window.c: Android video output module
3 *****************************************************************************
4 * Copyright (C) 2014 VLC authors and VideoLAN
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>
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.
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.
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 *****************************************************************************/
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>
40 #include "android_window.h"
43 /*****************************************************************************
45 *****************************************************************************/
47 #define CHROMA_TEXT N_("Chroma used")
48 #define CHROMA_LONGTEXT N_(\
49 "Force use of a specific chroma for output. Default is RGB32.")
51 #define CFG_PREFIX "androidsurface-"
52 static int Open (vlc_object_t *);
53 static void Close(vlc_object_t *);
56 set_category(CAT_VIDEO)
57 set_subcategory(SUBCAT_VIDEO_VOUT)
58 set_shortname("android_window")
59 set_description(N_("Android video output"))
60 set_capability("vout display", 260)
61 add_shortcut("androidwindow", "android")
62 set_callbacks(Open, Close)
65 /*****************************************************************************
67 *****************************************************************************/
69 #define THREAD_NAME "android_window"
70 extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
71 extern void jni_detach_thread();
73 extern jobject jni_LockAndGetAndroidJavaSurface();
74 extern jobject jni_LockAndGetSubtitlesSurface();
75 extern void jni_UnlockAndroidSurface();
77 extern bool jni_IsVideoPlayerActivityCreated();
78 extern void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
79 extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
80 extern int jni_GetWindowSize(int *width, int *height);
82 static const vlc_fourcc_t subpicture_chromas[] =
88 static picture_pool_t *Pool (vout_display_t *, unsigned);
89 static void Prepare(vout_display_t *, picture_t *, subpicture_t *);
90 static void Display(vout_display_t *, picture_t *, subpicture_t *);
91 static int Control(vout_display_t *, int, va_list);
93 typedef struct android_window android_window;
99 unsigned int i_pic_count;
100 unsigned int i_min_undequeued;
105 ANativeWindow *p_handle;
106 native_window_priv *p_handle_priv;
109 typedef struct buffer_bounds buffer_bounds;
116 struct vout_display_sys_t
118 picture_pool_t *pool;
121 int i_display_height;
124 native_window_api_t anw;
125 native_window_priv_api_t anwp;
128 android_window *p_window;
129 android_window *p_sub_window;
132 filter_t *p_spu_blend;
133 picture_t *p_sub_pic;
134 buffer_bounds *p_sub_buffer_bounds;
135 bool b_sub_pic_locked;
137 bool b_has_subpictures;
142 static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
144 unsigned int i_width, i_height;
145 unsigned int i_sar_num = 1, i_sar_den = 1;
146 video_format_t rot_fmt;
148 video_format_ApplyRotation(&rot_fmt, p_fmt);
150 if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
151 i_sar_num = rot_fmt.i_sar_num;
152 i_sar_den = rot_fmt.i_sar_den;
155 i_width = rot_fmt.i_visible_width;
156 i_height = rot_fmt.i_visible_height;
158 i_width = rot_fmt.i_width;
159 i_height = rot_fmt.i_height;
162 jni_SetSurfaceLayout(i_width, i_height,
163 rot_fmt.i_visible_width,
164 rot_fmt.i_visible_height,
170 static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt)
173 picture_resource_t rsc;
174 picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
176 if (unlikely(p_picsys == NULL))
179 p_picsys->p_vd_sys = sys;
181 memset(&rsc, 0, sizeof(picture_resource_t));
182 rsc.p_sys = p_picsys,
184 p_pic = picture_NewFromResource(fmt, &rsc);
193 static void FixSubtitleFormat(vout_display_sys_t *sys)
195 video_format_t *p_subfmt = &sys->p_sub_window->fmt;
197 int i_width, i_height;
198 int i_display_width, i_display_height;
201 video_format_ApplyRotation(&fmt, &sys->p_window->fmt);
203 if (fmt.i_visible_width == 0 || fmt.i_visible_height == 0) {
204 i_width = fmt.i_width;
205 i_height = fmt.i_height;
207 i_width = fmt.i_visible_width;
208 i_height = fmt.i_visible_height;
211 if (fmt.i_sar_num > 0 && fmt.i_sar_den > 0) {
212 if (fmt.i_sar_num >= fmt.i_sar_den)
213 i_width = i_width * fmt.i_sar_num / fmt.i_sar_den;
215 i_height = i_height * fmt.i_sar_den / fmt.i_sar_num;
218 if (sys->p_window->i_angle == 90 || sys->p_window->i_angle == 180) {
219 i_display_width = sys->i_display_height;
220 i_display_height = sys->i_display_width;
221 aspect = i_height / (double) i_width;
223 i_display_width = sys->i_display_width;
224 i_display_height = sys->i_display_height;
225 aspect = i_width / (double) i_height;
228 if (i_display_width / aspect < i_display_height) {
229 i_width = i_display_width;
230 i_height = i_display_width / aspect;
232 i_width = i_display_height * aspect;
233 i_height = i_display_height;
237 p_subfmt->i_visible_width = i_width;
239 p_subfmt->i_visible_height = i_height;
240 p_subfmt->i_x_offset = 0;
241 p_subfmt->i_y_offset = 0;
242 p_subfmt->i_sar_num = 1;
243 p_subfmt->i_sar_den = 1;
244 sys->b_sub_invalid = true;
247 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
248 static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
250 /* according to document of android.graphics.ImageFormat.YV12 */
251 int i_stride = ALIGN_16_PIXELS(i_in_stride);
252 int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
254 p_picture->p->i_pitch = i_stride;
256 /* Fill chroma planes for planar YUV */
257 for (int n = 1; n < p_picture->i_planes; n++)
259 const plane_t *o = &p_picture->p[n-1];
260 plane_t *p = &p_picture->p[n];
262 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
263 p->i_pitch = i_c_stride;
264 p->i_lines = p_picture->format.i_height / 2;
266 Explicitly set the padding lines of the picture to black (127 for YUV)
267 since they might be used by Android during rescaling.
269 int visible_lines = p_picture->format.i_visible_height / 2;
270 if (visible_lines < p->i_lines)
271 memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
274 if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
276 uint8_t *p_tmp = p_picture->p[1].p_pixels;
277 p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
278 p_picture->p[2].p_pixels = p_tmp;
282 static android_window *AndroidWindow_New(vout_display_sys_t *sys,
283 video_format_t *p_fmt,
286 android_window *p_window = calloc(1, sizeof(android_window));
291 p_window->b_opaque = p_fmt->i_chroma == VLC_CODEC_ANDROID_OPAQUE;
292 if (!p_window->b_opaque) {
293 p_window->b_use_priv = sys->b_has_anwp && b_use_priv;
295 p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
296 if (p_window->i_android_hal == -1) {
302 switch (p_fmt->orientation)
304 case ORIENT_ROTATED_90:
305 p_window->i_angle = 90;
307 case ORIENT_ROTATED_180:
308 p_window->i_angle = 180;
310 case ORIENT_ROTATED_270:
311 p_window->i_angle = 270;
314 p_window->i_angle = 0;
316 if (p_window->b_use_priv)
317 p_window->fmt = *p_fmt;
319 video_format_ApplyRotation(&p_window->fmt, p_fmt);
320 p_window->i_pic_count = 1;
324 static void AndroidWindow_Destroy(vout_display_sys_t *sys,
325 android_window *p_window)
327 if (p_window->p_handle_priv)
328 sys->anwp.disconnect(p_window->p_handle_priv);
329 if (p_window->p_handle)
330 sys->anw.winRelease(p_window->p_handle);
334 static int AndroidWindow_UpdateCrop(vout_display_sys_t *sys,
335 android_window *p_window)
337 if (!p_window->p_handle_priv)
340 return sys->anwp.setCrop(p_window->p_handle_priv,
341 p_window->fmt.i_x_offset,
342 p_window->fmt.i_y_offset,
343 p_window->fmt.i_visible_width,
344 p_window->fmt.i_visible_height);
347 static int AndroidWindow_SetSurface(vout_display_sys_t *sys,
348 android_window *p_window,
351 if (p_window->p_handle && jsurf != p_window->jsurf) {
352 if (p_window->p_handle_priv) {
353 sys->anwp.disconnect(p_window->p_handle_priv);
354 p_window->p_handle_priv = NULL;
356 sys->anw.winRelease(p_window->p_handle);
357 p_window->p_handle = NULL;
360 p_window->jsurf = jsurf;
361 if (!p_window->p_handle && !p_window->b_opaque) {
364 jni_attach_thread(&p_env, THREAD_NAME);
367 p_window->p_handle = sys->anw.winFromSurface(p_env, p_window->jsurf);
369 if (!p_window->p_handle)
376 static int AndroidWindow_SetupANWP(vout_display_sys_t *sys,
377 android_window *p_window)
379 unsigned int i_max_buffer_count = 0;
381 if (!p_window->p_handle_priv)
382 p_window->p_handle_priv = sys->anwp.connect(p_window->p_handle);
384 if (!p_window->p_handle_priv)
387 if (sys->anwp.setup(p_window->p_handle_priv,
388 p_window->fmt.i_width, p_window->fmt.i_height,
389 p_window->i_android_hal,
393 sys->anwp.getMinUndequeued(p_window->p_handle_priv,
394 &p_window->i_min_undequeued);
396 sys->anwp.getMaxBufferCount(p_window->p_handle_priv, &i_max_buffer_count);
398 if ((p_window->i_min_undequeued + p_window->i_pic_count) >
400 p_window->i_pic_count = i_max_buffer_count - p_window->i_min_undequeued;
402 if (sys->anwp.setBufferCount(p_window->p_handle_priv,
403 p_window->i_pic_count +
404 p_window->i_min_undequeued) != 0)
407 if (sys->anwp.setOrientation(p_window->p_handle_priv,
408 p_window->i_angle) != 0)
411 AndroidWindow_UpdateCrop(sys, p_window);
415 if (p_window->p_handle_priv) {
416 sys->anwp.disconnect(p_window->p_handle_priv);
417 p_window->p_handle_priv = NULL;
419 p_window->b_use_priv = false;
420 if (p_window->i_angle != 0)
421 video_format_ApplyRotation(&p_window->fmt, &p_window->fmt);
425 static int AndroidWindow_ConfigureSurface(vout_display_sys_t *sys,
426 android_window *p_window)
432 * anw.setBuffersGeometry and anwp.setup are broken before ics.
433 * use jni_ConfigureSurface to configure the surface on the java side
435 * jni_ConfigureSurface return -1 when you don't need to call it (ie, after
437 * if jni_ConfigureSurface succeed, you need to get a new surface handle.
438 * That's why AndroidWindow_SetSurface is called again here.
440 err = jni_ConfigureSurface(p_window->jsurf,
441 p_window->fmt.i_width,
442 p_window->fmt.i_height,
443 p_window->i_android_hal,
447 jobject jsurf = p_window->jsurf;
448 p_window->jsurf = NULL;
449 if (AndroidWindow_SetSurface(sys, p_window, jsurf) != 0)
457 static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
458 android_window *p_window)
460 p_window->i_pic_count = 1;
461 p_window->i_min_undequeued = 0;
463 return sys->anw.setBuffersGeometry(p_window->p_handle,
464 p_window->fmt.i_width,
465 p_window->fmt.i_height,
466 p_window->i_android_hal);
469 static int AndroidWindow_Setup(vout_display_sys_t *sys,
470 android_window *p_window,
471 unsigned int i_pic_count)
473 if (i_pic_count != 0)
474 p_window->i_pic_count = i_pic_count;
476 if (!p_window->b_opaque) {
478 picture_t *p_pic = PictureAlloc(sys, &p_window->fmt);
480 // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
481 align_pixels = (16 / p_pic->p[0].i_pixel_pitch) - 1;
482 p_window->fmt.i_height = p_pic->format.i_height;
483 p_window->fmt.i_width = (p_pic->format.i_width + align_pixels) & ~align_pixels;
484 picture_Release(p_pic);
487 if (AndroidWindow_ConfigureSurface(sys, p_window) != 0)
490 if (p_window->b_opaque) {
491 sys->p_window->i_pic_count = 31; // TODO
492 sys->p_window->i_min_undequeued = 0;
496 if (!p_window->b_use_priv
497 || AndroidWindow_SetupANWP(sys, p_window) != 0) {
498 if (AndroidWindow_SetupANW(sys, p_window) != 0)
505 static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
506 android_window *p_window,
510 picture_sys_t *p_picsys = p_pic->p_sys;
512 if (p_window->b_use_priv) {
513 void *p_handle = p_picsys->priv.sw.p_handle;
515 if (p_handle == NULL)
518 sys->anwp.unlockData(p_window->p_handle_priv, p_handle, b_render);
520 sys->anw.unlockAndPost(p_window->p_handle);
523 static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
524 android_window *p_window,
527 picture_sys_t *p_picsys = p_pic->p_sys;
529 if (p_window->b_use_priv) {
533 err = sys->anwp.lockData(p_window->p_handle_priv,
535 &p_picsys->priv.sw.buf);
538 p_picsys->priv.sw.p_handle = p_handle;
540 if (sys->anw.winLock(p_window->p_handle,
541 &p_picsys->priv.sw.buf, NULL) != 0)
544 if (p_picsys->priv.sw.buf.width < 0 ||
545 p_picsys->priv.sw.buf.height < 0 ||
546 (unsigned)p_picsys->priv.sw.buf.width < p_window->fmt.i_width ||
547 (unsigned)p_picsys->priv.sw.buf.height < p_window->fmt.i_height) {
548 AndroidWindow_UnlockPicture(sys, p_window, p_pic, false);
552 p_pic->p[0].p_pixels = p_picsys->priv.sw.buf.bits;
553 p_pic->p[0].i_lines = p_picsys->priv.sw.buf.height;
554 p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->priv.sw.buf.stride;
556 if (p_picsys->priv.sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
557 SetupPictureYV12(p_pic, p_picsys->priv.sw.buf.stride);
562 static int SetupWindowSurface(vout_display_sys_t *sys, unsigned i_pic_count)
565 jobject jsurf = jni_LockAndGetAndroidJavaSurface();
566 err = AndroidWindow_SetSurface(sys, sys->p_window, jsurf);
567 jni_UnlockAndroidSurface();
568 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_window, i_pic_count) : err;
572 static int SetupWindowSubtitleSurface(vout_display_sys_t *sys)
575 jobject jsurf = jni_LockAndGetSubtitlesSurface();
576 err = AndroidWindow_SetSurface(sys, sys->p_sub_window, jsurf);
577 jni_UnlockAndroidSurface();
578 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_sub_window, 1) : err;
582 static void SetRGBMask(video_format_t *p_fmt)
584 switch(p_fmt->i_chroma) {
585 case VLC_CODEC_RGB16:
586 p_fmt->i_bmask = 0x0000001f;
587 p_fmt->i_gmask = 0x000007e0;
588 p_fmt->i_rmask = 0x0000f800;
591 case VLC_CODEC_RGB32:
593 p_fmt->i_rmask = 0x000000ff;
594 p_fmt->i_gmask = 0x0000ff00;
595 p_fmt->i_bmask = 0x00ff0000;
600 static void SendEventDisplaySize(vout_display_t *vd)
602 vout_display_sys_t *sys = vd->sys;
603 int i_display_width, i_display_height;
605 if (jni_GetWindowSize(&i_display_width, &i_display_height) == 0
606 && i_display_width != 0 && i_display_height != 0
607 && (i_display_width != sys->i_display_width
608 || i_display_height != sys->i_display_height))
609 vout_display_SendEventDisplaySize(vd, i_display_width,
613 static int Open(vlc_object_t *p_this)
615 vout_display_t *vd = (vout_display_t*)p_this;
616 vout_display_sys_t *sys;
617 video_format_t sub_fmt;
619 if (vout_display_IsWindowed(vd))
622 /* XXX: android_window use a surface created by VideoPlayerActivity to
623 * alloc pictures. Don't try to open the vout if this activity is not
624 * created. This need to be replaced by something like var_CreateGetAddress
625 * (vd, "drawable-android") in the future. */
626 if (!jni_IsVideoPlayerActivityCreated())
629 /* Allocate structure */
630 vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
634 sys->p_library = LoadNativeWindowAPI(&sys->anw);
635 if (!sys->p_library) {
636 msg_Err(vd, "Could not initialize NativeWindow API.");
641 if (LoadNativeWindowPrivAPI(&sys->anwp) == 0)
642 sys->b_has_anwp = true;
644 msg_Warn(vd, "Could not initialize NativeWindow Priv API.");
647 sys->i_display_width = vd->cfg->display.width;
648 sys->i_display_height = vd->cfg->display.height;
650 if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
652 char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
654 vd->fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
657 vd->fmt.i_chroma = VLC_CODEC_RGB32;
659 switch(vd->fmt.i_chroma) {
661 /* avoid swscale usage by asking for I420 instead since the
662 * vout already has code to swap the buffers */
663 vd->fmt.i_chroma = VLC_CODEC_I420;
666 case VLC_CODEC_RGB16:
667 case VLC_CODEC_RGB32:
669 SetRGBMask(&vd->fmt);
670 video_format_FixRgb(&vd->fmt);
677 sys->p_window = AndroidWindow_New(sys, &vd->fmt, true);
681 if (SetupWindowSurface(sys, 0) != 0)
684 /* use software rotation if we don't use private anw */
685 if (!sys->p_window->b_opaque && !sys->p_window->b_use_priv)
686 video_format_ApplyRotation(&vd->fmt, &vd->fmt);
688 msg_Dbg(vd, "using %s", sys->p_window->b_opaque ? "opaque" :
689 (sys->p_window->b_use_priv ? "ANWP" : "ANW"));
691 video_format_ApplyRotation(&sub_fmt, &vd->fmt);
692 sub_fmt.i_chroma = subpicture_chromas[0];
693 SetRGBMask(&sub_fmt);
694 video_format_FixRgb(&sub_fmt);
695 sys->p_sub_window = AndroidWindow_New(sys, &sub_fmt, false);
696 if (!sys->p_sub_window)
698 FixSubtitleFormat(sys);
700 /* Export the subpicture capability of this vout. */
701 vd->info.subpicture_chromas = subpicture_chromas;
703 /* Setup vout_display */
705 vd->prepare = Prepare;
706 vd->display = Display;
707 vd->control = Control;
710 /* Fix initial state */
711 vout_display_SendEventFullscreen(vd, true);
712 SendEventDisplaySize(vd);
721 static void Close(vlc_object_t *p_this)
723 vout_display_t *vd = (vout_display_t *)p_this;
724 vout_display_sys_t *sys = vd->sys;
730 picture_pool_Release(sys->pool);
732 AndroidWindow_Destroy(sys, sys->p_window);
735 picture_Release(sys->p_sub_pic);
736 if (sys->p_spu_blend)
737 filter_DeleteBlend(sys->p_spu_blend);
738 free(sys->p_sub_buffer_bounds);
739 if (sys->p_sub_window)
740 AndroidWindow_Destroy(sys, sys->p_sub_window);
743 dlclose(sys->p_library);
748 static int DefaultLockPicture(picture_t *p_pic)
750 picture_sys_t *p_picsys = p_pic->p_sys;
751 vout_display_sys_t *sys = p_picsys->p_vd_sys;
753 return AndroidWindow_LockPicture(sys, sys->p_window, p_pic);
756 static void DefaultUnlockPicture(picture_t *p_pic, bool b_render)
758 picture_sys_t *p_picsys = p_pic->p_sys;
759 vout_display_sys_t *sys = p_picsys->p_vd_sys;
761 AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic, b_render);
764 static void UnlockPicture(picture_t *p_pic, bool b_render)
766 picture_sys_t *p_picsys = p_pic->p_sys;
768 if (p_picsys->b_locked && p_picsys->pf_unlock_pic)
769 p_picsys->pf_unlock_pic(p_pic, b_render);
770 p_picsys->b_locked = false;
773 static int PoolLockPicture(picture_t *p_pic)
775 picture_sys_t *p_picsys = p_pic->p_sys;
777 if (p_picsys->pf_lock_pic && p_picsys->pf_lock_pic(p_pic) != 0)
779 p_picsys->b_locked = true;
783 static void PoolUnlockPicture(picture_t *p_pic)
785 UnlockPicture(p_pic, false);
788 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
790 vout_display_sys_t *sys = vd->sys;
791 picture_pool_t *pool = NULL;
792 picture_t **pp_pics = NULL;
795 msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
796 if (SetupWindowSurface(sys, requested_count) != 0)
799 requested_count = sys->p_window->i_pic_count;
800 msg_Dbg(vd, "PoolAlloc: got %d frames", requested_count);
802 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
804 pp_pics = calloc(requested_count, sizeof(picture_t));
806 for (i = 0; i < requested_count; i++)
808 picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt);
811 if (!sys->p_window->b_opaque) {
812 p_pic->p_sys->pf_lock_pic = DefaultLockPicture;
813 p_pic->p_sys->pf_unlock_pic = DefaultUnlockPicture;
819 picture_pool_configuration_t pool_cfg;
820 memset(&pool_cfg, 0, sizeof(pool_cfg));
821 pool_cfg.picture_count = requested_count;
822 pool_cfg.picture = pp_pics;
823 pool_cfg.lock = PoolLockPicture;
824 pool_cfg.unlock = PoolUnlockPicture;
825 pool = picture_pool_NewExtended(&pool_cfg);
828 if (!pool && pp_pics) {
829 for (unsigned j = 0; j < i; j++)
830 picture_Release(pp_pics[j]);
836 static void SubtitleRegionToBounds(subpicture_t *subpicture,
840 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
843 new_bounds.left = r->i_x;
844 new_bounds.top = r->i_y;
845 new_bounds.right = r->fmt.i_visible_width + r->i_x;
846 new_bounds.bottom = r->fmt.i_visible_height + r->i_y;
847 if (r == &subpicture->p_region[0])
848 *p_out_bounds = new_bounds;
850 if (p_out_bounds->left > new_bounds.left)
851 p_out_bounds->left = new_bounds.left;
852 if (p_out_bounds->right < new_bounds.right)
853 p_out_bounds->right = new_bounds.right;
854 if (p_out_bounds->top > new_bounds.top)
855 p_out_bounds->top = new_bounds.top;
856 if (p_out_bounds->bottom < new_bounds.bottom)
857 p_out_bounds->bottom = new_bounds.bottom;
861 p_out_bounds->left = p_out_bounds->top = 0;
862 p_out_bounds->right = p_out_bounds->bottom = 0;
866 static void SubtitleGetDirtyBounds(vout_display_t *vd,
867 subpicture_t *subpicture,
870 vout_display_sys_t *sys = vd->sys;
872 bool b_found = false;
874 /* Try to find last bounds set by current locked buffer.
875 * Indeed, even if we can lock only one buffer at a time, differents
876 * buffers can be locked. This functions will find the last bounds set by
877 * the current buffer. */
878 if (sys->p_sub_buffer_bounds) {
879 for (; sys->p_sub_buffer_bounds[i].p_pixels != NULL; ++i) {
880 buffer_bounds *p_bb = &sys->p_sub_buffer_bounds[i];
881 if (p_bb->p_pixels == sys->p_sub_pic->p[0].p_pixels) {
882 *p_out_bounds = p_bb->bounds;
890 || p_out_bounds->left < 0
891 || p_out_bounds->right < 0
892 || (unsigned int) p_out_bounds->right > sys->p_sub_pic->format.i_width
893 || p_out_bounds->bottom < 0
894 || p_out_bounds->top < 0
895 || (unsigned int) p_out_bounds->top > sys->p_sub_pic->format.i_height)
897 /* default is full picture */
898 p_out_bounds->left = 0;
899 p_out_bounds->top = 0;
900 p_out_bounds->right = sys->p_sub_pic->format.i_width;
901 p_out_bounds->bottom = sys->p_sub_pic->format.i_height;
904 /* buffer not found, add it to the array */
905 if (!sys->p_sub_buffer_bounds
906 || sys->p_sub_buffer_bounds[i].p_pixels == NULL) {
907 buffer_bounds *p_bb = realloc(sys->p_sub_buffer_bounds,
908 (i + 2) * sizeof(buffer_bounds));
910 sys->p_sub_buffer_bounds = p_bb;
911 sys->p_sub_buffer_bounds[i].p_pixels = sys->p_sub_pic->p[0].p_pixels;
912 sys->p_sub_buffer_bounds[i+1].p_pixels = NULL;
916 /* set buffer bounds */
917 if (sys->p_sub_buffer_bounds
918 && sys->p_sub_buffer_bounds[i].p_pixels != NULL)
919 SubtitleRegionToBounds(subpicture, &sys->p_sub_buffer_bounds[i].bounds);
922 static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture)
924 vout_display_sys_t *sys = vd->sys;
930 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
931 AddMD5(&hash, &r->i_x, sizeof(r->i_x));
932 AddMD5(&hash, &r->i_y, sizeof(r->i_y));
933 AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
934 AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
935 AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
936 AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
937 const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
938 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
940 for (unsigned int y = 0; y < r->fmt.i_visible_height; y++)
941 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
945 if (!memcmp(hash.buf, sys->hash, 16))
947 memcpy(sys->hash, hash.buf, 16);
949 if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
952 sys->b_sub_pic_locked = true;
954 /* Clear the subtitles surface. */
955 SubtitleGetDirtyBounds(vd, subpicture, &memset_bounds);
956 const int x_pixels_offset = memset_bounds.left
957 * sys->p_sub_pic->p[0].i_pixel_pitch;
958 const int i_line_size = (memset_bounds.right - memset_bounds.left)
959 * sys->p_sub_pic->p->i_pixel_pitch;
960 for (int y = memset_bounds.top; y < memset_bounds.bottom; y++)
961 memset(&sys->p_sub_pic->p[0].p_pixels[y * sys->p_sub_pic->p[0].i_pitch
962 + x_pixels_offset], 0, i_line_size);
965 picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
968 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
970 vout_display_sys_t *sys = vd->sys;
972 if (sys->pool == NULL)
973 sys->pool = PoolAlloc(vd, requested_count);
977 static void Prepare(vout_display_t *vd, picture_t *picture,
978 subpicture_t *subpicture)
980 vout_display_sys_t *sys = vd->sys;
983 SendEventDisplaySize(vd);
986 if (sys->b_sub_invalid) {
987 sys->b_sub_invalid = false;
988 if (sys->p_sub_pic) {
989 picture_Release(sys->p_sub_pic);
990 sys->p_sub_pic = NULL;
992 if (sys->p_spu_blend) {
993 filter_DeleteBlend(sys->p_spu_blend);
994 sys->p_spu_blend = NULL;
996 free(sys->p_sub_buffer_bounds);
997 sys->p_sub_buffer_bounds = NULL;
1000 if (!sys->p_sub_pic && SetupWindowSubtitleSurface(sys) == 0)
1001 sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt);
1002 if (!sys->p_spu_blend)
1003 sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
1004 &sys->p_sub_pic->format);
1006 if (sys->p_sub_pic && sys->p_spu_blend)
1007 sys->b_has_subpictures = true;
1009 /* As long as no subpicture was received, do not call
1010 SubpictureDisplay since JNI calls and clearing the subtitles
1011 surface are expensive operations. */
1012 if (sys->b_has_subpictures)
1014 SubpicturePrepare(vd, subpicture);
1017 /* The surface has been cleared and there is no new
1018 subpicture to upload, do not clear again until a new
1019 subpicture is received. */
1020 sys->b_has_subpictures = false;
1025 static void Display(vout_display_t *vd, picture_t *picture,
1026 subpicture_t *subpicture)
1028 vout_display_sys_t *sys = vd->sys;
1030 /* refcount lowers to 0, and pool_cfg.unlock is called */
1031 UnlockPicture(picture, true);
1032 picture_Release(picture);
1034 if (sys->b_sub_pic_locked) {
1035 sys->b_sub_pic_locked = false;
1036 AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic, true);
1039 subpicture_Delete(subpicture);
1042 static void CopySourceAspect(video_format_t *p_dest,
1043 const video_format_t *p_src)
1045 p_dest->i_sar_num = p_src->i_sar_num;
1046 p_dest->i_sar_den = p_src->i_sar_den;
1049 static int Control(vout_display_t *vd, int query, va_list args)
1051 vout_display_sys_t *sys = vd->sys;
1054 case VOUT_DISPLAY_HIDE_MOUSE:
1055 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
1057 case VOUT_DISPLAY_RESET_PICTURES:
1059 if (sys->p_window->b_opaque)
1060 return VLC_EGENERIC;
1062 msg_Dbg(vd, "resetting pictures");
1064 if (sys->pool != NULL)
1066 picture_pool_Release(sys->pool);
1071 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
1072 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
1073 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
1075 if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
1076 || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1077 const video_format_t *source;
1079 msg_Dbg(vd, "change source crop/aspect");
1080 source = va_arg(args, const video_format_t *);
1082 if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1083 video_format_CopyCrop(&sys->p_window->fmt, source);
1084 AndroidWindow_UpdateCrop(sys, sys->p_window);
1086 CopySourceAspect(&sys->p_window->fmt, source);
1088 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
1090 const vout_display_cfg_t *cfg;
1092 cfg = va_arg(args, const vout_display_cfg_t *);
1094 sys->i_display_width = cfg->display.width;
1095 sys->i_display_height = cfg->display.height;
1096 msg_Dbg(vd, "change display size: %dx%d", sys->i_display_width,
1097 sys->i_display_height);
1099 FixSubtitleFormat(sys);
1104 msg_Warn(vd, "Unknown request in android_window");
1105 case VOUT_DISPLAY_CHANGE_ZOOM:
1106 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
1107 return VLC_EGENERIC;