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", 200)
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 void jni_SetSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
78 extern int jni_ConfigureSurface(jobject jsurf, int width, int height, int hal, bool *configured);
79 extern int jni_GetWindowSize(int *width, int *height);
81 static const vlc_fourcc_t subpicture_chromas[] =
87 static picture_pool_t *Pool (vout_display_t *, unsigned);
88 static void Prepare(vout_display_t *, picture_t *, subpicture_t *);
89 static void Display(vout_display_t *, picture_t *, subpicture_t *);
90 static int Control(vout_display_t *, int, va_list);
92 typedef struct android_window android_window;
98 unsigned int i_pic_count;
99 unsigned int i_min_undequeued;
104 ANativeWindow *p_handle;
105 native_window_priv *p_handle_priv;
108 typedef struct buffer_bounds buffer_bounds;
115 struct vout_display_sys_t
117 picture_pool_t *pool;
120 int i_display_height;
123 native_window_api_t anw;
124 native_window_priv_api_t anwp;
127 android_window *p_window;
128 android_window *p_sub_window;
131 filter_t *p_spu_blend;
132 picture_t *p_sub_pic;
133 buffer_bounds *p_sub_buffer_bounds;
134 bool b_sub_pic_locked;
136 bool b_has_subpictures;
141 static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
143 unsigned int i_width, i_height;
144 unsigned int i_sar_num = 1, i_sar_den = 1;
145 video_format_t rot_fmt;
147 video_format_ApplyRotation(&rot_fmt, p_fmt);
149 if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
150 i_sar_num = rot_fmt.i_sar_num;
151 i_sar_den = rot_fmt.i_sar_den;
154 i_width = rot_fmt.i_visible_width;
155 i_height = rot_fmt.i_visible_height;
157 i_width = rot_fmt.i_width;
158 i_height = rot_fmt.i_height;
161 jni_SetSurfaceLayout(i_width, i_height,
162 rot_fmt.i_visible_width,
163 rot_fmt.i_visible_height,
169 static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt)
172 picture_resource_t rsc;
173 picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
175 if (unlikely(p_picsys == NULL))
178 p_picsys->p_vd_sys = sys;
180 memset(&rsc, 0, sizeof(picture_resource_t));
181 rsc.p_sys = p_picsys,
183 p_pic = picture_NewFromResource(fmt, &rsc);
192 static void FixSubtitleFormat(vout_display_sys_t *sys)
194 video_format_t *p_subfmt = &sys->p_sub_window->fmt;
196 int i_width, i_height;
197 int i_display_width, i_display_height;
200 video_format_ApplyRotation(&fmt, &sys->p_window->fmt);
202 if (fmt.i_visible_width == 0 || fmt.i_visible_height == 0) {
203 i_width = fmt.i_width;
204 i_height = fmt.i_height;
206 i_width = fmt.i_visible_width;
207 i_height = fmt.i_visible_height;
210 if (fmt.i_sar_num > 0 && fmt.i_sar_den > 0) {
211 if (fmt.i_sar_num >= fmt.i_sar_den)
212 i_width = i_width * fmt.i_sar_num / fmt.i_sar_den;
214 i_height = i_height * fmt.i_sar_den / fmt.i_sar_num;
217 if (sys->p_window->i_angle == 90 || sys->p_window->i_angle == 180) {
218 i_display_width = sys->i_display_height;
219 i_display_height = sys->i_display_width;
220 aspect = i_height / (double) i_width;
222 i_display_width = sys->i_display_width;
223 i_display_height = sys->i_display_height;
224 aspect = i_width / (double) i_height;
227 if (i_display_width / aspect < i_display_height) {
228 i_width = i_display_width;
229 i_height = i_display_width / aspect;
231 i_width = i_display_height * aspect;
232 i_height = i_display_height;
236 p_subfmt->i_visible_width = i_width;
238 p_subfmt->i_visible_height = i_height;
239 p_subfmt->i_x_offset = 0;
240 p_subfmt->i_y_offset = 0;
241 p_subfmt->i_sar_num = 1;
242 p_subfmt->i_sar_den = 1;
243 sys->b_sub_invalid = true;
246 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
247 static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
249 /* according to document of android.graphics.ImageFormat.YV12 */
250 int i_stride = ALIGN_16_PIXELS(i_in_stride);
251 int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
253 p_picture->p->i_pitch = i_stride;
255 /* Fill chroma planes for planar YUV */
256 for (int n = 1; n < p_picture->i_planes; n++)
258 const plane_t *o = &p_picture->p[n-1];
259 plane_t *p = &p_picture->p[n];
261 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
262 p->i_pitch = i_c_stride;
263 p->i_lines = p_picture->format.i_height / 2;
265 Explicitly set the padding lines of the picture to black (127 for YUV)
266 since they might be used by Android during rescaling.
268 int visible_lines = p_picture->format.i_visible_height / 2;
269 if (visible_lines < p->i_lines)
270 memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
273 if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
275 uint8_t *p_tmp = p_picture->p[1].p_pixels;
276 p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
277 p_picture->p[2].p_pixels = p_tmp;
281 static android_window *AndroidWindow_New(vout_display_sys_t *sys,
282 video_format_t *p_fmt,
285 android_window *p_window = calloc(1, sizeof(android_window));
290 p_window->b_opaque = p_fmt->i_chroma == VLC_CODEC_ANDROID_OPAQUE;
291 if (!p_window->b_opaque) {
292 p_window->b_use_priv = sys->b_has_anwp && b_use_priv;
294 p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
295 if (p_window->i_android_hal == -1) {
301 switch (p_fmt->orientation)
303 case ORIENT_ROTATED_90:
304 p_window->i_angle = 90;
306 case ORIENT_ROTATED_180:
307 p_window->i_angle = 180;
309 case ORIENT_ROTATED_270:
310 p_window->i_angle = 270;
313 p_window->i_angle = 0;
315 if (p_window->b_use_priv)
316 p_window->fmt = *p_fmt;
318 video_format_ApplyRotation(&p_window->fmt, p_fmt);
319 p_window->i_pic_count = 1;
323 static void AndroidWindow_Destroy(vout_display_sys_t *sys,
324 android_window *p_window)
326 if (p_window->p_handle_priv)
327 sys->anwp.disconnect(p_window->p_handle_priv);
328 if (p_window->p_handle)
329 sys->anw.winRelease(p_window->p_handle);
333 static int AndroidWindow_UpdateCrop(vout_display_sys_t *sys,
334 android_window *p_window)
336 if (!p_window->p_handle_priv)
339 return sys->anwp.setCrop(p_window->p_handle_priv,
340 p_window->fmt.i_x_offset,
341 p_window->fmt.i_y_offset,
342 p_window->fmt.i_visible_width,
343 p_window->fmt.i_visible_height);
346 static int AndroidWindow_SetSurface(vout_display_sys_t *sys,
347 android_window *p_window,
350 if (p_window->p_handle && jsurf != p_window->jsurf) {
351 if (p_window->p_handle_priv) {
352 sys->anwp.disconnect(p_window->p_handle_priv);
353 p_window->p_handle_priv = NULL;
355 sys->anw.winRelease(p_window->p_handle);
356 p_window->p_handle = NULL;
359 p_window->jsurf = jsurf;
360 if (!p_window->p_handle && !p_window->b_opaque) {
363 jni_attach_thread(&p_env, THREAD_NAME);
366 p_window->p_handle = sys->anw.winFromSurface(p_env, p_window->jsurf);
368 if (!p_window->p_handle)
375 static int AndroidWindow_SetupANWP(vout_display_sys_t *sys,
376 android_window *p_window)
378 unsigned int i_max_buffer_count = 0;
380 if (!p_window->p_handle_priv)
381 p_window->p_handle_priv = sys->anwp.connect(p_window->p_handle);
383 if (!p_window->p_handle_priv)
386 if (sys->anwp.setup(p_window->p_handle_priv,
387 p_window->fmt.i_width, p_window->fmt.i_height,
388 p_window->i_android_hal,
392 sys->anwp.getMinUndequeued(p_window->p_handle_priv,
393 &p_window->i_min_undequeued);
395 sys->anwp.getMaxBufferCount(p_window->p_handle_priv, &i_max_buffer_count);
397 if ((p_window->i_min_undequeued + p_window->i_pic_count) >
399 p_window->i_pic_count = i_max_buffer_count - p_window->i_min_undequeued;
401 if (sys->anwp.setBufferCount(p_window->p_handle_priv,
402 p_window->i_pic_count +
403 p_window->i_min_undequeued) != 0)
406 if (sys->anwp.setOrientation(p_window->p_handle_priv,
407 p_window->i_angle) != 0)
410 AndroidWindow_UpdateCrop(sys, p_window);
414 if (p_window->p_handle_priv) {
415 sys->anwp.disconnect(p_window->p_handle_priv);
416 p_window->p_handle_priv = NULL;
418 p_window->b_use_priv = false;
419 if (p_window->i_angle != 0)
420 video_format_ApplyRotation(&p_window->fmt, &p_window->fmt);
424 static int AndroidWindow_ConfigureSurface(vout_display_sys_t *sys,
425 android_window *p_window)
431 * anw.setBuffersGeometry and anwp.setup are broken before ics.
432 * use jni_ConfigureSurface to configure the surface on the java side
434 * jni_ConfigureSurface return -1 when you don't need to call it (ie, after
436 * if jni_ConfigureSurface succeed, you need to get a new surface handle.
437 * That's why AndroidWindow_SetSurface is called again here.
439 err = jni_ConfigureSurface(p_window->jsurf,
440 p_window->fmt.i_width,
441 p_window->fmt.i_height,
442 p_window->i_android_hal,
446 jobject jsurf = p_window->jsurf;
447 p_window->jsurf = NULL;
448 if (AndroidWindow_SetSurface(sys, p_window, jsurf) != 0)
456 static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
457 android_window *p_window)
459 p_window->i_pic_count = 1;
460 p_window->i_min_undequeued = 0;
462 return sys->anw.setBuffersGeometry(p_window->p_handle,
463 p_window->fmt.i_width,
464 p_window->fmt.i_height,
465 p_window->i_android_hal);
468 static int AndroidWindow_Setup(vout_display_sys_t *sys,
469 android_window *p_window,
470 unsigned int i_pic_count)
472 if (i_pic_count != 0)
473 p_window->i_pic_count = i_pic_count;
475 if (!p_window->b_opaque) {
477 picture_t *p_pic = PictureAlloc(sys, &p_window->fmt);
479 // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
480 align_pixels = (16 / p_pic->p[0].i_pixel_pitch) - 1;
481 p_window->fmt.i_height = p_pic->format.i_height;
482 p_window->fmt.i_width = (p_pic->format.i_width + align_pixels) & ~align_pixels;
483 picture_Release(p_pic);
486 if (AndroidWindow_ConfigureSurface(sys, p_window) != 0)
489 if (p_window->b_opaque) {
490 sys->p_window->i_pic_count = 31; // TODO
491 sys->p_window->i_min_undequeued = 0;
495 if (!p_window->b_use_priv
496 || AndroidWindow_SetupANWP(sys, p_window) != 0) {
497 if (AndroidWindow_SetupANW(sys, p_window) != 0)
504 static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
505 android_window *p_window,
509 picture_sys_t *p_picsys = p_pic->p_sys;
511 if (p_window->b_use_priv) {
512 void *p_handle = p_picsys->priv.sw.p_handle;
514 if (p_handle == NULL)
517 sys->anwp.unlockData(p_window->p_handle_priv, p_handle, b_render);
519 sys->anw.unlockAndPost(p_window->p_handle);
522 static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
523 android_window *p_window,
526 picture_sys_t *p_picsys = p_pic->p_sys;
528 if (p_window->b_use_priv) {
532 err = sys->anwp.lockData(p_window->p_handle_priv,
534 &p_picsys->priv.sw.buf);
537 p_picsys->priv.sw.p_handle = p_handle;
539 if (sys->anw.winLock(p_window->p_handle,
540 &p_picsys->priv.sw.buf, NULL) != 0)
543 if (p_picsys->priv.sw.buf.width < 0 ||
544 p_picsys->priv.sw.buf.height < 0 ||
545 (unsigned)p_picsys->priv.sw.buf.width < p_window->fmt.i_width ||
546 (unsigned)p_picsys->priv.sw.buf.height < p_window->fmt.i_height) {
547 AndroidWindow_UnlockPicture(sys, p_window, p_pic, false);
551 p_pic->p[0].p_pixels = p_picsys->priv.sw.buf.bits;
552 p_pic->p[0].i_lines = p_picsys->priv.sw.buf.height;
553 p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->priv.sw.buf.stride;
555 if (p_picsys->priv.sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
556 SetupPictureYV12(p_pic, p_picsys->priv.sw.buf.stride);
561 static int SetupWindowSurface(vout_display_sys_t *sys, unsigned i_pic_count)
564 jobject jsurf = jni_LockAndGetAndroidJavaSurface();
565 err = AndroidWindow_SetSurface(sys, sys->p_window, jsurf);
566 jni_UnlockAndroidSurface();
567 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_window, i_pic_count) : err;
571 static int SetupWindowSubtitleSurface(vout_display_sys_t *sys)
574 jobject jsurf = jni_LockAndGetSubtitlesSurface();
575 err = AndroidWindow_SetSurface(sys, sys->p_sub_window, jsurf);
576 jni_UnlockAndroidSurface();
577 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_sub_window, 1) : err;
581 static void SetRGBMask(video_format_t *p_fmt)
583 switch(p_fmt->i_chroma) {
584 case VLC_CODEC_RGB16:
585 p_fmt->i_bmask = 0x0000001f;
586 p_fmt->i_gmask = 0x000007e0;
587 p_fmt->i_rmask = 0x0000f800;
590 case VLC_CODEC_RGB32:
592 p_fmt->i_rmask = 0x000000ff;
593 p_fmt->i_gmask = 0x0000ff00;
594 p_fmt->i_bmask = 0x00ff0000;
599 static void SendEventDisplaySize(vout_display_t *vd)
601 vout_display_sys_t *sys = vd->sys;
602 int i_display_width, i_display_height;
604 if (jni_GetWindowSize(&i_display_width, &i_display_height) == 0
605 && i_display_width != 0 && i_display_height != 0
606 && (i_display_width != sys->i_display_width
607 || i_display_height != sys->i_display_height))
608 vout_display_SendEventDisplaySize(vd, i_display_width,
612 static int Open(vlc_object_t *p_this)
614 vout_display_t *vd = (vout_display_t*)p_this;
615 vout_display_sys_t *sys;
616 video_format_t sub_fmt;
618 if (vout_display_IsWindowed(vd))
621 /* Allocate structure */
622 vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
626 sys->p_library = LoadNativeWindowAPI(&sys->anw);
627 if (!sys->p_library) {
628 msg_Err(vd, "Could not initialize NativeWindow API.");
633 if (LoadNativeWindowPrivAPI(&sys->anwp) == 0)
634 sys->b_has_anwp = true;
636 msg_Warn(vd, "Could not initialize NativeWindow Priv API.");
639 sys->i_display_width = vd->cfg->display.width;
640 sys->i_display_height = vd->cfg->display.height;
642 if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
644 char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
646 vd->fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
649 vd->fmt.i_chroma = VLC_CODEC_RGB32;
651 switch(vd->fmt.i_chroma) {
653 /* avoid swscale usage by asking for I420 instead since the
654 * vout already has code to swap the buffers */
655 vd->fmt.i_chroma = VLC_CODEC_I420;
658 case VLC_CODEC_RGB16:
659 case VLC_CODEC_RGB32:
661 SetRGBMask(&vd->fmt);
662 video_format_FixRgb(&vd->fmt);
669 sys->p_window = AndroidWindow_New(sys, &vd->fmt, true);
673 if (SetupWindowSurface(sys, 0) != 0)
676 /* use software rotation if we don't use private anw */
677 if (!sys->p_window->b_opaque && !sys->p_window->b_use_priv)
678 video_format_ApplyRotation(&vd->fmt, &vd->fmt);
680 msg_Dbg(vd, "using %s", sys->p_window->b_opaque ? "opaque" :
681 (sys->p_window->b_use_priv ? "ANWP" : "ANW"));
683 video_format_ApplyRotation(&sub_fmt, &vd->fmt);
684 sub_fmt.i_chroma = subpicture_chromas[0];
685 SetRGBMask(&sub_fmt);
686 video_format_FixRgb(&sub_fmt);
687 sys->p_sub_window = AndroidWindow_New(sys, &sub_fmt, false);
688 if (!sys->p_sub_window)
690 FixSubtitleFormat(sys);
692 /* Export the subpicture capability of this vout. */
693 vd->info.subpicture_chromas = subpicture_chromas;
695 /* Setup vout_display */
697 vd->prepare = Prepare;
698 vd->display = Display;
699 vd->control = Control;
702 /* Fix initial state */
703 vout_display_SendEventFullscreen(vd, true);
704 SendEventDisplaySize(vd);
713 static void Close(vlc_object_t *p_this)
715 vout_display_t *vd = (vout_display_t *)p_this;
716 vout_display_sys_t *sys = vd->sys;
722 picture_pool_Release(sys->pool);
724 AndroidWindow_Destroy(sys, sys->p_window);
727 picture_Release(sys->p_sub_pic);
728 if (sys->p_spu_blend)
729 filter_DeleteBlend(sys->p_spu_blend);
730 free(sys->p_sub_buffer_bounds);
731 if (sys->p_sub_window)
732 AndroidWindow_Destroy(sys, sys->p_sub_window);
735 dlclose(sys->p_library);
740 static int DefaultLockPicture(picture_t *p_pic)
742 picture_sys_t *p_picsys = p_pic->p_sys;
743 vout_display_sys_t *sys = p_picsys->p_vd_sys;
745 return AndroidWindow_LockPicture(sys, sys->p_window, p_pic);
748 static void DefaultUnlockPicture(picture_t *p_pic, bool b_render)
750 picture_sys_t *p_picsys = p_pic->p_sys;
751 vout_display_sys_t *sys = p_picsys->p_vd_sys;
753 AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic, b_render);
756 static void UnlockPicture(picture_t *p_pic, bool b_render)
758 picture_sys_t *p_picsys = p_pic->p_sys;
760 if (p_picsys->b_locked && p_picsys->pf_unlock_pic)
761 p_picsys->pf_unlock_pic(p_pic, b_render);
762 p_picsys->b_locked = false;
765 static int PoolLockPicture(picture_t *p_pic)
767 picture_sys_t *p_picsys = p_pic->p_sys;
769 if (p_picsys->pf_lock_pic && p_picsys->pf_lock_pic(p_pic) != 0)
771 p_picsys->b_locked = true;
775 static void PoolUnlockPicture(picture_t *p_pic)
777 UnlockPicture(p_pic, false);
780 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
782 vout_display_sys_t *sys = vd->sys;
783 picture_pool_t *pool = NULL;
784 picture_t **pp_pics = NULL;
787 msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
788 if (SetupWindowSurface(sys, requested_count) != 0)
791 requested_count = sys->p_window->i_pic_count;
792 msg_Dbg(vd, "PoolAlloc: got %d frames", requested_count);
794 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
796 pp_pics = calloc(requested_count, sizeof(picture_t));
798 for (i = 0; i < requested_count; i++)
800 picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt);
803 if (!sys->p_window->b_opaque) {
804 p_pic->p_sys->pf_lock_pic = DefaultLockPicture;
805 p_pic->p_sys->pf_unlock_pic = DefaultUnlockPicture;
811 picture_pool_configuration_t pool_cfg;
812 memset(&pool_cfg, 0, sizeof(pool_cfg));
813 pool_cfg.picture_count = requested_count;
814 pool_cfg.picture = pp_pics;
815 pool_cfg.lock = PoolLockPicture;
816 pool_cfg.unlock = PoolUnlockPicture;
817 pool = picture_pool_NewExtended(&pool_cfg);
820 if (!pool && pp_pics) {
821 for (unsigned j = 0; j < i; j++)
822 picture_Release(pp_pics[j]);
828 static void SubtitleRegionToBounds(subpicture_t *subpicture,
832 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
835 new_bounds.left = r->i_x;
836 new_bounds.top = r->i_y;
837 new_bounds.right = r->fmt.i_visible_width + r->i_x;
838 new_bounds.bottom = r->fmt.i_visible_height + r->i_y;
839 if (r == &subpicture->p_region[0])
840 *p_out_bounds = new_bounds;
842 if (p_out_bounds->left > new_bounds.left)
843 p_out_bounds->left = new_bounds.left;
844 if (p_out_bounds->right < new_bounds.right)
845 p_out_bounds->right = new_bounds.right;
846 if (p_out_bounds->top > new_bounds.top)
847 p_out_bounds->top = new_bounds.top;
848 if (p_out_bounds->bottom < new_bounds.bottom)
849 p_out_bounds->bottom = new_bounds.bottom;
853 p_out_bounds->left = p_out_bounds->top = 0;
854 p_out_bounds->right = p_out_bounds->bottom = 0;
858 static void SubtitleGetDirtyBounds(vout_display_t *vd,
859 subpicture_t *subpicture,
862 vout_display_sys_t *sys = vd->sys;
864 bool b_found = false;
866 /* Try to find last bounds set by current locked buffer.
867 * Indeed, even if we can lock only one buffer at a time, differents
868 * buffers can be locked. This functions will find the last bounds set by
869 * the current buffer. */
870 if (sys->p_sub_buffer_bounds) {
871 for (; sys->p_sub_buffer_bounds[i].p_pixels != NULL; ++i) {
872 buffer_bounds *p_bb = &sys->p_sub_buffer_bounds[i];
873 if (p_bb->p_pixels == sys->p_sub_pic->p[0].p_pixels) {
874 *p_out_bounds = p_bb->bounds;
882 || p_out_bounds->left < 0
883 || p_out_bounds->right < 0
884 || (unsigned int) p_out_bounds->right > sys->p_sub_pic->format.i_width
885 || p_out_bounds->bottom < 0
886 || p_out_bounds->top < 0
887 || (unsigned int) p_out_bounds->top > sys->p_sub_pic->format.i_height)
889 /* default is full picture */
890 p_out_bounds->left = 0;
891 p_out_bounds->top = 0;
892 p_out_bounds->right = sys->p_sub_pic->format.i_width;
893 p_out_bounds->bottom = sys->p_sub_pic->format.i_height;
896 /* buffer not found, add it to the array */
897 if (!sys->p_sub_buffer_bounds
898 || sys->p_sub_buffer_bounds[i].p_pixels == NULL) {
899 buffer_bounds *p_bb = realloc(sys->p_sub_buffer_bounds,
900 (i + 2) * sizeof(buffer_bounds));
902 sys->p_sub_buffer_bounds = p_bb;
903 sys->p_sub_buffer_bounds[i].p_pixels = sys->p_sub_pic->p[0].p_pixels;
904 sys->p_sub_buffer_bounds[i+1].p_pixels = NULL;
908 /* set buffer bounds */
909 if (sys->p_sub_buffer_bounds
910 && sys->p_sub_buffer_bounds[i].p_pixels != NULL)
911 SubtitleRegionToBounds(subpicture, &sys->p_sub_buffer_bounds[i].bounds);
914 static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture)
916 vout_display_sys_t *sys = vd->sys;
922 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
923 AddMD5(&hash, &r->i_x, sizeof(r->i_x));
924 AddMD5(&hash, &r->i_y, sizeof(r->i_y));
925 AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
926 AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
927 AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
928 AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
929 const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
930 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
932 for (unsigned int y = 0; y < r->fmt.i_visible_height; y++)
933 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
937 if (!memcmp(hash.buf, sys->hash, 16))
939 memcpy(sys->hash, hash.buf, 16);
941 if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
944 sys->b_sub_pic_locked = true;
946 /* Clear the subtitles surface. */
947 SubtitleGetDirtyBounds(vd, subpicture, &memset_bounds);
948 const int x_pixels_offset = memset_bounds.left
949 * sys->p_sub_pic->p[0].i_pixel_pitch;
950 const int i_line_size = (memset_bounds.right - memset_bounds.left)
951 * sys->p_sub_pic->p->i_pixel_pitch;
952 for (int y = memset_bounds.top; y < memset_bounds.bottom; y++)
953 memset(&sys->p_sub_pic->p[0].p_pixels[y * sys->p_sub_pic->p[0].i_pitch
954 + x_pixels_offset], 0, i_line_size);
957 picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
960 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
962 vout_display_sys_t *sys = vd->sys;
964 if (sys->pool == NULL)
965 sys->pool = PoolAlloc(vd, requested_count);
969 static void Prepare(vout_display_t *vd, picture_t *picture,
970 subpicture_t *subpicture)
972 vout_display_sys_t *sys = vd->sys;
975 SendEventDisplaySize(vd);
978 if (sys->b_sub_invalid) {
979 sys->b_sub_invalid = false;
980 if (sys->p_sub_pic) {
981 picture_Release(sys->p_sub_pic);
982 sys->p_sub_pic = NULL;
984 if (sys->p_spu_blend) {
985 filter_DeleteBlend(sys->p_spu_blend);
986 sys->p_spu_blend = NULL;
988 free(sys->p_sub_buffer_bounds);
989 sys->p_sub_buffer_bounds = NULL;
992 if (!sys->p_sub_pic && SetupWindowSubtitleSurface(sys) == 0)
993 sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt);
994 if (!sys->p_spu_blend)
995 sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
996 &sys->p_sub_pic->format);
998 if (sys->p_sub_pic && sys->p_spu_blend)
999 sys->b_has_subpictures = true;
1001 /* As long as no subpicture was received, do not call
1002 SubpictureDisplay since JNI calls and clearing the subtitles
1003 surface are expensive operations. */
1004 if (sys->b_has_subpictures)
1006 SubpicturePrepare(vd, subpicture);
1009 /* The surface has been cleared and there is no new
1010 subpicture to upload, do not clear again until a new
1011 subpicture is received. */
1012 sys->b_has_subpictures = false;
1017 static void Display(vout_display_t *vd, picture_t *picture,
1018 subpicture_t *subpicture)
1020 vout_display_sys_t *sys = vd->sys;
1022 /* refcount lowers to 0, and pool_cfg.unlock is called */
1023 UnlockPicture(picture, true);
1024 picture_Release(picture);
1026 if (sys->b_sub_pic_locked) {
1027 sys->b_sub_pic_locked = false;
1028 AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic, true);
1031 subpicture_Delete(subpicture);
1034 static void CopySourceAspect(video_format_t *p_dest,
1035 const video_format_t *p_src)
1037 p_dest->i_sar_num = p_src->i_sar_num;
1038 p_dest->i_sar_den = p_src->i_sar_den;
1041 static int Control(vout_display_t *vd, int query, va_list args)
1043 vout_display_sys_t *sys = vd->sys;
1046 case VOUT_DISPLAY_HIDE_MOUSE:
1047 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
1049 case VOUT_DISPLAY_RESET_PICTURES:
1051 if (sys->p_window->b_opaque)
1052 return VLC_EGENERIC;
1054 msg_Dbg(vd, "resetting pictures");
1056 if (sys->pool != NULL)
1058 picture_pool_Release(sys->pool);
1063 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
1064 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
1065 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
1067 if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
1068 || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1069 const video_format_t *source;
1071 msg_Dbg(vd, "change source crop/aspect");
1072 source = va_arg(args, const video_format_t *);
1074 if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
1075 video_format_CopyCrop(&sys->p_window->fmt, source);
1076 AndroidWindow_UpdateCrop(sys, sys->p_window);
1078 CopySourceAspect(&sys->p_window->fmt, source);
1080 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
1082 const vout_display_cfg_t *cfg;
1084 cfg = va_arg(args, const vout_display_cfg_t *);
1086 sys->i_display_width = cfg->display.width;
1087 sys->i_display_height = cfg->display.height;
1088 msg_Dbg(vd, "change display size: %dx%d", sys->i_display_width,
1089 sys->i_display_height);
1091 FixSubtitleFormat(sys);
1096 msg_Warn(vd, "Unknown request in android_window");
1097 case VOUT_DISPLAY_CHANGE_ZOOM:
1098 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
1099 return VLC_EGENERIC;