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);
80 static const vlc_fourcc_t subpicture_chromas[] =
86 static picture_pool_t *Pool (vout_display_t *, unsigned);
87 static void Display(vout_display_t *, picture_t *, subpicture_t *);
88 static int Control(vout_display_t *, int, va_list);
90 typedef struct android_window android_window;
96 unsigned int i_pic_count;
97 unsigned int i_min_undequeued;
102 ANativeWindow *p_handle;
103 native_window_priv *p_handle_priv;
106 struct vout_display_sys_t
108 picture_pool_t *pool;
111 native_window_api_t anw;
112 native_window_priv_api_t anwp;
115 android_window *p_window;
116 android_window *p_sub_window;
118 filter_t *p_spu_blend;
119 picture_t *p_sub_pic;
121 bool b_has_subpictures;
126 static int UpdateWindowSize(video_format_t *p_fmt, bool b_cropped)
128 unsigned int i_width, i_height;
129 unsigned int i_sar_num = 1, i_sar_den = 1;
130 video_format_t rot_fmt;
132 video_format_ApplyRotation(&rot_fmt, p_fmt);
134 if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
135 i_sar_num = rot_fmt.i_sar_num;
136 i_sar_den = rot_fmt.i_sar_den;
139 i_width = rot_fmt.i_visible_width;
140 i_height = rot_fmt.i_visible_height;
142 i_width = rot_fmt.i_width;
143 i_height = rot_fmt.i_height;
146 jni_SetSurfaceLayout(i_width, i_height,
147 rot_fmt.i_visible_width,
148 rot_fmt.i_visible_height,
154 static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt)
157 picture_resource_t rsc;
158 picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
160 if (unlikely(p_picsys == NULL))
163 p_picsys->p_vd_sys = sys;
165 memset(&rsc, 0, sizeof(picture_resource_t));
166 rsc.p_sys = p_picsys,
168 p_pic = picture_NewFromResource(fmt, &rsc);
177 static void FixSubtitleFormat(vout_display_sys_t *sys)
179 video_format_t *p_fmt = &sys->p_sub_window->fmt;
181 if (p_fmt->i_visible_width == 0 || p_fmt->i_visible_height == 0) {
182 p_fmt->i_visible_width = p_fmt->i_width;
183 p_fmt->i_visible_height = p_fmt->i_height;
185 if (p_fmt->i_sar_num > 0 && p_fmt->i_sar_den > 0) {
186 if (p_fmt->i_sar_num >= p_fmt->i_sar_den)
187 p_fmt->i_width = (int64_t)p_fmt->i_visible_width * p_fmt->i_sar_num / p_fmt->i_sar_den;
189 p_fmt->i_height = (int64_t)p_fmt->i_visible_height * p_fmt->i_sar_den / p_fmt->i_sar_num;
190 p_fmt->i_sar_num = 1;
191 p_fmt->i_sar_den = 1;
193 p_fmt->i_width = p_fmt->i_visible_width;
194 p_fmt->i_height = p_fmt->i_visible_height;
196 p_fmt->i_x_offset = 0;
197 p_fmt->i_y_offset = 0;
200 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
201 static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
203 /* according to document of android.graphics.ImageFormat.YV12 */
204 int i_stride = ALIGN_16_PIXELS(i_in_stride);
205 int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
207 p_picture->p->i_pitch = i_stride;
209 /* Fill chroma planes for planar YUV */
210 for (int n = 1; n < p_picture->i_planes; n++)
212 const plane_t *o = &p_picture->p[n-1];
213 plane_t *p = &p_picture->p[n];
215 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
216 p->i_pitch = i_c_stride;
217 p->i_lines = p_picture->format.i_height / 2;
219 Explicitly set the padding lines of the picture to black (127 for YUV)
220 since they might be used by Android during rescaling.
222 int visible_lines = p_picture->format.i_visible_height / 2;
223 if (visible_lines < p->i_lines)
224 memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
227 if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
229 uint8_t *p_tmp = p_picture->p[1].p_pixels;
230 p_picture->p[1].p_pixels = p_picture->p[2].p_pixels;
231 p_picture->p[2].p_pixels = p_tmp;
235 static android_window *AndroidWindow_New(vout_display_sys_t *sys,
236 video_format_t *p_fmt,
239 android_window *p_window = calloc(1, sizeof(android_window));
244 p_window->b_opaque = p_fmt->i_chroma == VLC_CODEC_ANDROID_OPAQUE;
245 if (!p_window->b_opaque) {
246 p_window->b_use_priv = sys->b_has_anwp && b_use_priv;
248 p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
249 if (p_window->i_android_hal == -1) {
255 if (p_window->b_use_priv) {
256 switch (p_fmt->orientation)
258 case ORIENT_ROTATED_90:
259 p_window->i_angle = 90;
261 case ORIENT_ROTATED_180:
262 p_window->i_angle = 180;
264 case ORIENT_ROTATED_270:
265 p_window->i_angle = 270;
268 p_window->i_angle = 0;
270 p_window->fmt = *p_fmt;
272 video_format_ApplyRotation(&p_window->fmt, p_fmt);
274 p_window->i_pic_count = 1;
278 static void AndroidWindow_Destroy(vout_display_sys_t *sys,
279 android_window *p_window)
281 if (p_window->p_handle_priv)
282 sys->anwp.disconnect(p_window->p_handle_priv);
283 if (p_window->p_handle)
284 sys->anw.winRelease(p_window->p_handle);
288 static int AndroidWindow_UpdateCrop(vout_display_sys_t *sys,
289 android_window *p_window)
291 if (!p_window->p_handle_priv)
294 return sys->anwp.setCrop(p_window->p_handle_priv,
295 p_window->fmt.i_x_offset,
296 p_window->fmt.i_y_offset,
297 p_window->fmt.i_visible_width,
298 p_window->fmt.i_visible_height);
301 static unsigned int AndroidWindow_GetPicCount(vout_display_sys_t *sys,
302 android_window *p_window)
305 return p_window->i_min_undequeued + p_window->i_pic_count;
308 static int AndroidWindow_SetSurface(vout_display_sys_t *sys,
309 android_window *p_window,
312 if (p_window->p_handle && jsurf != p_window->jsurf) {
313 if (p_window->p_handle_priv) {
314 sys->anwp.disconnect(p_window->p_handle_priv);
315 p_window->p_handle_priv = NULL;
317 sys->anw.winRelease(p_window->p_handle);
318 p_window->p_handle = NULL;
321 p_window->jsurf = jsurf;
322 if (!p_window->p_handle && !p_window->b_opaque) {
325 jni_attach_thread(&p_env, THREAD_NAME);
328 p_window->p_handle = sys->anw.winFromSurface(p_env, p_window->jsurf);
330 if (!p_window->p_handle)
337 static int AndroidWindow_SetupANWP(vout_display_sys_t *sys,
338 android_window *p_window)
340 unsigned int i_max_buffer_count = 0;
342 if (!p_window->p_handle_priv)
343 p_window->p_handle_priv = sys->anwp.connect(p_window->p_handle);
345 if (!p_window->p_handle_priv)
348 if (sys->anwp.setup(p_window->p_handle_priv,
349 p_window->fmt.i_width, p_window->fmt.i_height,
350 p_window->i_android_hal,
354 sys->anwp.getMinUndequeued(p_window->p_handle_priv,
355 &p_window->i_min_undequeued);
357 sys->anwp.getMaxBufferCount(p_window->p_handle_priv, &i_max_buffer_count);
359 if ((p_window->i_min_undequeued + p_window->i_pic_count) >
361 p_window->i_pic_count = i_max_buffer_count - p_window->i_min_undequeued;
363 if (sys->anwp.setBufferCount(p_window->p_handle_priv,
364 AndroidWindow_GetPicCount(sys, p_window)) != 0)
367 if (sys->anwp.setOrientation(p_window->p_handle_priv,
368 p_window->i_angle) != 0)
371 AndroidWindow_UpdateCrop(sys, p_window);
375 if (p_window->p_handle_priv) {
376 sys->anwp.disconnect(p_window->p_handle_priv);
377 p_window->p_handle_priv = NULL;
379 p_window->b_use_priv = false;
380 if (p_window->i_angle != 0)
381 video_format_ApplyRotation(&p_window->fmt, &p_window->fmt);
382 p_window->i_angle = 0;
386 static int AndroidWindow_ConfigureSurface(vout_display_sys_t *sys,
387 android_window *p_window)
393 * anw.setBuffersGeometry and anwp.setup are broken before ics.
394 * use jni_ConfigureSurface to configure the surface on the java side
396 * jni_ConfigureSurface return -1 when you don't need to call it (ie, after
398 * if jni_ConfigureSurface succeed, you need to get a new surface handle.
399 * That's why AndroidWindow_SetSurface is called again here.
401 err = jni_ConfigureSurface(p_window->jsurf,
402 p_window->fmt.i_width,
403 p_window->fmt.i_height,
404 p_window->i_android_hal,
408 jobject jsurf = p_window->jsurf;
409 p_window->jsurf = NULL;
410 if (AndroidWindow_SetSurface(sys, p_window, jsurf) != 0)
418 static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
419 android_window *p_window)
421 p_window->i_pic_count = 1;
422 p_window->i_min_undequeued = 0;
424 return sys->anw.setBuffersGeometry(p_window->p_handle,
425 p_window->fmt.i_width,
426 p_window->fmt.i_height,
427 p_window->i_android_hal);
430 static int AndroidWindow_Setup(vout_display_sys_t *sys,
431 android_window *p_window,
432 unsigned int i_pic_count)
434 if (i_pic_count != 0)
435 p_window->i_pic_count = i_pic_count;
437 if (!p_window->b_opaque) {
439 picture_t *p_pic = PictureAlloc(sys, &p_window->fmt);
441 // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
442 align_pixels = (16 / p_pic->p[0].i_pixel_pitch) - 1;
443 p_window->fmt.i_height = p_pic->format.i_height;
444 p_window->fmt.i_width = (p_pic->format.i_width + align_pixels) & ~align_pixels;
445 picture_Release(p_pic);
448 if (AndroidWindow_ConfigureSurface(sys, p_window) != 0)
451 if (p_window->b_opaque) {
452 sys->p_window->i_pic_count = 31; // TODO
453 sys->p_window->i_min_undequeued = 0;
457 if (!p_window->b_use_priv
458 || AndroidWindow_SetupANWP(sys, p_window) != 0) {
459 if (AndroidWindow_SetupANW(sys, p_window) != 0)
466 static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
467 android_window *p_window,
471 picture_sys_t *p_picsys = p_pic->p_sys;
473 if (p_window->b_use_priv) {
474 void *p_handle = p_picsys->priv.sw.p_handle;
476 if (p_handle == NULL)
479 sys->anwp.unlockData(p_window->p_handle_priv, p_handle, b_render);
481 sys->anw.unlockAndPost(p_window->p_handle);
484 static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
485 android_window *p_window,
488 picture_sys_t *p_picsys = p_pic->p_sys;
490 if (p_window->b_use_priv) {
494 err = sys->anwp.lockData(p_window->p_handle_priv,
496 &p_picsys->priv.sw.buf);
499 p_picsys->priv.sw.p_handle = p_handle;
501 if (sys->anw.winLock(p_window->p_handle,
502 &p_picsys->priv.sw.buf, NULL) != 0)
505 if (p_picsys->priv.sw.buf.width < 0 ||
506 p_picsys->priv.sw.buf.height < 0 ||
507 (unsigned)p_picsys->priv.sw.buf.width < p_window->fmt.i_width ||
508 (unsigned)p_picsys->priv.sw.buf.height < p_window->fmt.i_height) {
509 AndroidWindow_UnlockPicture(sys, p_window, p_pic, false);
513 p_pic->p[0].p_pixels = p_picsys->priv.sw.buf.bits;
514 p_pic->p[0].i_lines = p_picsys->priv.sw.buf.height;
515 p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->priv.sw.buf.stride;
517 if (p_picsys->priv.sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
518 SetupPictureYV12(p_pic, p_picsys->priv.sw.buf.stride);
523 static int SetupWindowSurface(vout_display_sys_t *sys, unsigned i_pic_count)
526 jobject jsurf = jni_LockAndGetAndroidJavaSurface();
527 err = AndroidWindow_SetSurface(sys, sys->p_window, jsurf);
528 jni_UnlockAndroidSurface();
529 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_window, i_pic_count) : err;
533 static int SetupWindowSubtitleSurface(vout_display_sys_t *sys)
536 jobject jsurf = jni_LockAndGetSubtitlesSurface();
537 err = AndroidWindow_SetSurface(sys, sys->p_sub_window, jsurf);
538 jni_UnlockAndroidSurface();
539 err = err == 0 ? AndroidWindow_Setup(sys, sys->p_sub_window, 1) : err;
543 static void SetRGBMask(video_format_t *p_fmt)
545 switch(p_fmt->i_chroma) {
546 case VLC_CODEC_RGB16:
547 p_fmt->i_bmask = 0x0000001f;
548 p_fmt->i_gmask = 0x000007e0;
549 p_fmt->i_rmask = 0x0000f800;
552 case VLC_CODEC_RGB32:
554 p_fmt->i_rmask = 0x000000ff;
555 p_fmt->i_gmask = 0x0000ff00;
556 p_fmt->i_bmask = 0x00ff0000;
561 static int Open(vlc_object_t *p_this)
563 vout_display_t *vd = (vout_display_t*)p_this;
564 vout_display_sys_t *sys;
565 video_format_t sub_fmt;
567 if (vout_display_IsWindowed(vd))
570 /* Allocate structure */
571 vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
575 sys->p_library = LoadNativeWindowAPI(&sys->anw);
576 if (!sys->p_library) {
577 msg_Err(vd, "Could not initialize NativeWindow API.");
582 if (LoadNativeWindowPrivAPI(&sys->anwp) == 0)
583 sys->b_has_anwp = true;
585 msg_Warn(vd, "Could not initialize NativeWindow Priv API.");
588 if (vd->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
590 char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
592 vd->fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
595 vd->fmt.i_chroma = VLC_CODEC_RGB32;
597 switch(vd->fmt.i_chroma) {
599 /* avoid swscale usage by asking for I420 instead since the
600 * vout already has code to swap the buffers */
601 vd->fmt.i_chroma = VLC_CODEC_I420;
604 case VLC_CODEC_RGB16:
605 case VLC_CODEC_RGB32:
607 SetRGBMask(&vd->fmt);
608 video_format_FixRgb(&vd->fmt);
615 sys->p_window = AndroidWindow_New(sys, &vd->fmt, true);
619 if (SetupWindowSurface(sys, 0) != 0)
622 /* use software rotation if we don't use private anw */
623 if (!sys->p_window->b_opaque && !sys->p_window->b_use_priv)
624 video_format_ApplyRotation(&vd->fmt, &vd->fmt);
626 msg_Dbg(vd, "using %s", sys->p_window->b_opaque ? "opaque" :
627 (sys->p_window->b_use_priv ? "ANWP" : "ANW"));
629 video_format_ApplyRotation(&sub_fmt, &vd->fmt);
630 sub_fmt.i_chroma = subpicture_chromas[0];
631 SetRGBMask(&sub_fmt);
632 video_format_FixRgb(&sub_fmt);
633 sys->p_sub_window = AndroidWindow_New(sys, &sub_fmt, false);
634 if (!sys->p_sub_window)
636 FixSubtitleFormat(sys);
638 /* Export the subpicture capability of this vout. */
639 vd->info.subpicture_chromas = subpicture_chromas;
641 /* Setup vout_display */
644 vd->display = Display;
645 vd->control = Control;
648 /* Fix initial state */
649 vout_display_SendEventFullscreen(vd, false);
658 static void Close(vlc_object_t *p_this)
660 vout_display_t *vd = (vout_display_t *)p_this;
661 vout_display_sys_t *sys = vd->sys;
667 picture_pool_Release(sys->pool);
669 AndroidWindow_Destroy(sys, sys->p_window);
672 picture_Release(sys->p_sub_pic);
673 if (sys->p_spu_blend)
674 filter_DeleteBlend(sys->p_spu_blend);
675 if (sys->p_sub_window)
676 AndroidWindow_Destroy(sys, sys->p_sub_window);
679 dlclose(sys->p_library);
684 static int DefaultLockPicture(picture_t *p_pic)
686 picture_sys_t *p_picsys = p_pic->p_sys;
687 vout_display_sys_t *sys = p_picsys->p_vd_sys;
689 return AndroidWindow_LockPicture(sys, sys->p_window, p_pic);
692 static void DefaultUnlockPicture(picture_t *p_pic, bool b_render)
694 picture_sys_t *p_picsys = p_pic->p_sys;
695 vout_display_sys_t *sys = p_picsys->p_vd_sys;
697 AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic, b_render);
700 static void UnlockPicture(picture_t *p_pic, bool b_render)
702 picture_sys_t *p_picsys = p_pic->p_sys;
704 if (p_picsys->b_locked && p_picsys->pf_unlock_pic)
705 p_picsys->pf_unlock_pic(p_pic, b_render);
706 p_picsys->b_locked = false;
709 static int PoolLockPicture(picture_t *p_pic)
711 picture_sys_t *p_picsys = p_pic->p_sys;
713 if (p_picsys->pf_lock_pic && p_picsys->pf_lock_pic(p_pic) != 0)
715 p_picsys->b_locked = true;
719 static void PoolUnlockPicture(picture_t *p_pic)
721 UnlockPicture(p_pic, false);
724 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
726 vout_display_sys_t *sys = vd->sys;
727 picture_pool_t *pool = NULL;
728 picture_t **pp_pics = NULL;
731 msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
732 if (SetupWindowSurface(sys, requested_count) != 0)
735 requested_count = AndroidWindow_GetPicCount(sys, sys->p_window);
736 msg_Dbg(vd, "PoolAlloc: got %d frames", requested_count);
738 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
740 pp_pics = calloc(requested_count, sizeof(picture_t));
742 for (i = 0; i < requested_count; i++)
744 picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt);
747 if (!sys->p_window->b_opaque) {
748 p_pic->p_sys->pf_lock_pic = DefaultLockPicture;
749 p_pic->p_sys->pf_unlock_pic = DefaultUnlockPicture;
755 picture_pool_configuration_t pool_cfg;
756 memset(&pool_cfg, 0, sizeof(pool_cfg));
757 pool_cfg.picture_count = requested_count;
758 pool_cfg.picture = pp_pics;
759 pool_cfg.lock = PoolLockPicture;
760 pool_cfg.unlock = PoolUnlockPicture;
761 pool = picture_pool_NewExtended(&pool_cfg);
764 if (!pool && pp_pics) {
765 for (unsigned j = 0; j < i; j++)
766 picture_Release(pp_pics[j]);
772 static void SubpictureDisplay(vout_display_t *vd, subpicture_t *subpicture)
774 vout_display_sys_t *sys = vd->sys;
779 for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
780 AddMD5(&hash, &r->i_x, sizeof(r->i_x));
781 AddMD5(&hash, &r->i_y, sizeof(r->i_y));
782 AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width));
783 AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height));
784 AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset));
785 AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset));
786 const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
787 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
789 for (unsigned int y = 0; y < r->fmt.i_visible_height; y++)
790 AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width);
794 if (!memcmp(hash.buf, sys->hash, 16))
796 memcpy(sys->hash, hash.buf, 16);
798 if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
801 /* Clear the subtitles surface. */
802 memset(sys->p_sub_pic->p[0].p_pixels, 0,
803 sys->p_sub_pic->p[0].i_pitch * sys->p_sub_pic->p[0].i_lines);
807 /* Allocate a blending filter if needed. */
808 if (unlikely(!sys->p_spu_blend))
809 sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
810 &sys->p_sub_pic->format);
811 picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
813 AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic, true);
816 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
818 vout_display_sys_t *sys = vd->sys;
820 if (sys->pool == NULL)
821 sys->pool = PoolAlloc(vd, requested_count);
825 static void Display(vout_display_t *vd, picture_t *picture,
826 subpicture_t *subpicture)
828 vout_display_sys_t *sys = vd->sys;
830 /* refcount lowers to 0, and pool_cfg.unlock is called */
831 UnlockPicture(picture, true);
832 picture_Release(picture);
835 if (!sys->p_sub_pic && SetupWindowSubtitleSurface(sys) == 0)
836 sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt);
839 sys->b_has_subpictures = true;
841 /* As long as no subpicture was received, do not call
842 SubpictureDisplay since JNI calls and clearing the subtitles
843 surface are expensive operations. */
844 if (sys->b_has_subpictures)
846 SubpictureDisplay(vd, subpicture);
849 /* The surface has been cleared and there is no new
850 subpicture to upload, do not clear again until a new
851 subpicture is received. */
852 sys->b_has_subpictures = false;
857 subpicture_Delete(subpicture);
860 static void CopySourceAspect(video_format_t *p_dest,
861 const video_format_t *p_src)
863 p_dest->i_sar_num = p_src->i_sar_num;
864 p_dest->i_sar_den = p_src->i_sar_den;
867 static int Control(vout_display_t *vd, int query, va_list args)
869 vout_display_sys_t *sys = vd->sys;
872 case VOUT_DISPLAY_HIDE_MOUSE:
874 case VOUT_DISPLAY_RESET_PICTURES:
876 if (sys->p_window->b_opaque)
879 msg_Dbg(vd, "resetting pictures");
881 if (sys->pool != NULL)
883 picture_pool_Release(sys->pool);
888 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
889 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
891 const video_format_t *source;
892 video_format_t sub_fmt;
894 msg_Dbg(vd, "change source crop/aspect");
896 source = va_arg(args, const video_format_t *);
897 video_format_ApplyRotation(&sub_fmt, source);
899 if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
900 video_format_CopyCrop(&sys->p_window->fmt, source);
901 AndroidWindow_UpdateCrop(sys, sys->p_window);
903 video_format_CopyCrop(&sys->p_sub_window->fmt, &sub_fmt);
905 CopySourceAspect(&sys->p_window->fmt, source);
907 CopySourceAspect(&sys->p_sub_window->fmt, &sub_fmt);
909 UpdateWindowSize(&sys->p_window->fmt, sys->p_window->b_use_priv);
910 FixSubtitleFormat(sys);
912 if (sys->p_sub_pic) {
913 picture_Release(sys->p_sub_pic);
914 sys->p_sub_pic = NULL;
916 if (sys->p_spu_blend) {
917 filter_DeleteBlend(sys->p_spu_blend);
918 sys->p_spu_blend = NULL;
924 msg_Warn(vd, "Unknown request in android_window");
925 case VOUT_DISPLAY_CHANGE_ZOOM:
926 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
927 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: