1 /*****************************************************************************
2 * display.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>
37 #include <vlc_opengl.h> /* for ClearSurface */
38 #include <GLES2/gl2.h> /* for ClearSurface */
45 /*****************************************************************************
47 *****************************************************************************/
49 #define CHROMA_TEXT "Chroma used"
50 #define CHROMA_LONGTEXT \
51 "Force use of a specific chroma for output. Default is RGB32."
53 #define CFG_PREFIX "android-display-"
54 static int Open(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
55 video_format_t
*fmtp
, vlc_video_context
*context
);
56 static int OpenOpaque(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
57 video_format_t
*fmtp
, vlc_video_context
*context
);
58 static void Close(vout_display_t
*vd
);
59 static void SubpicturePrepare(vout_display_t
*vd
, subpicture_t
*subpicture
);
62 set_category(CAT_VIDEO
)
63 set_subcategory(SUBCAT_VIDEO_VOUT
)
64 set_description("Android video output")
65 set_capability("vout display", 260)
66 add_shortcut("android-display")
67 add_string(CFG_PREFIX
"chroma", NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
, true)
68 set_callbacks(Open
, Close
)
70 set_description("Android opaque video output")
71 set_capability("vout display", 280)
72 add_shortcut("android-opaque")
73 set_callbacks(OpenOpaque
, Close
)
76 /*****************************************************************************
78 *****************************************************************************/
80 static const vlc_fourcc_t subpicture_chromas
[] =
86 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
87 static void Prepare(vout_display_t
*, picture_t
*, subpicture_t
*, vlc_tick_t
);
88 static void Display(vout_display_t
*, picture_t
*);
89 static int Control(vout_display_t
*, int, va_list);
91 typedef struct android_window android_window
;
97 unsigned int i_pic_count
;
98 unsigned int i_min_undequeued
;
102 ANativeWindow
*p_surface
;
106 typedef struct buffer_bounds buffer_bounds
;
113 struct vout_display_sys_t
115 vout_window_t
*embed
;
116 picture_pool_t
*pool
;
119 int i_display_height
;
121 AWindowHandler
*p_awh
;
122 native_window_api_t
*anw
;
124 android_window
*p_window
;
125 android_window
*p_sub_window
;
129 filter_t
*p_spu_blend
;
130 picture_t
*p_sub_pic
;
131 buffer_bounds
*p_sub_buffer_bounds
;
132 int64_t i_sub_last_order
;
133 ARect sub_last_region
;
135 bool b_has_subpictures
;
138 #define PRIV_WINDOW_FORMAT_YV12 0x32315659
140 static inline int ChromaToAndroidHal(vlc_fourcc_t i_chroma
)
145 return PRIV_WINDOW_FORMAT_YV12
;
146 case VLC_CODEC_RGB16
:
147 return WINDOW_FORMAT_RGB_565
;
148 case VLC_CODEC_RGB32
:
149 return WINDOW_FORMAT_RGBX_8888
;
151 return WINDOW_FORMAT_RGBA_8888
;
157 static int UpdateVideoSize(vout_display_sys_t
*sys
, video_format_t
*p_fmt
)
159 unsigned int i_width
, i_height
;
160 unsigned int i_sar_num
= 1, i_sar_den
= 1;
161 video_format_t rot_fmt
;
163 video_format_ApplyRotation(&rot_fmt
, p_fmt
);
165 if (rot_fmt
.i_sar_num
!= 0 && rot_fmt
.i_sar_den
!= 0) {
166 i_sar_num
= rot_fmt
.i_sar_num
;
167 i_sar_den
= rot_fmt
.i_sar_den
;
169 i_width
= rot_fmt
.i_width
;
170 i_height
= rot_fmt
.i_height
;
172 AWindowHandler_setVideoLayout(sys
->p_awh
, i_width
, i_height
,
173 rot_fmt
.i_visible_width
,
174 rot_fmt
.i_visible_height
,
175 i_sar_num
, i_sar_den
);
179 static picture_t
*PictureAlloc(vout_display_sys_t
*sys
, video_format_t
*fmt
,
183 picture_resource_t rsc
;
184 picture_sys_t
*p_picsys
= calloc(1, sizeof(*p_picsys
));
186 if (unlikely(p_picsys
== NULL
))
190 memset(&rsc
, 0, sizeof(picture_resource_t
));
191 rsc
.p_sys
= p_picsys
;
195 p_picsys
->hw
.b_vd_ref
= true;
196 p_picsys
->hw
.p_surface
= sys
->p_window
->p_surface
;
197 p_picsys
->hw
.p_jsurface
= sys
->p_window
->p_jsurface
;
198 p_picsys
->hw
.i_index
= -1;
199 vlc_mutex_init(&p_picsys
->hw
.lock
);
200 rsc
.pf_destroy
= AndroidOpaquePicture_DetachVout
;
203 p_picsys
->sw
.p_vd_sys
= sys
;
205 p_pic
= picture_NewFromResource(fmt
, &rsc
);
214 static void FixSubtitleFormat(vout_display_sys_t
*sys
)
216 video_format_t
*p_subfmt
;
218 int i_width
, i_height
;
219 int i_video_width
, i_video_height
;
220 int i_display_width
, i_display_height
;
223 if (!sys
->p_sub_window
)
225 p_subfmt
= &sys
->p_sub_window
->fmt
;
227 video_format_ApplyRotation(&fmt
, &sys
->p_window
->fmt
);
229 if (fmt
.i_visible_width
== 0 || fmt
.i_visible_height
== 0) {
230 i_video_width
= fmt
.i_width
;
231 i_video_height
= fmt
.i_height
;
233 i_video_width
= fmt
.i_visible_width
;
234 i_video_height
= fmt
.i_visible_height
;
237 if (fmt
.i_sar_num
> 0 && fmt
.i_sar_den
> 0) {
238 if (fmt
.i_sar_num
>= fmt
.i_sar_den
)
239 i_video_width
= i_video_width
* fmt
.i_sar_num
/ fmt
.i_sar_den
;
241 i_video_height
= i_video_height
* fmt
.i_sar_den
/ fmt
.i_sar_num
;
244 if (sys
->p_window
->i_angle
== 90 || sys
->p_window
->i_angle
== 180) {
245 i_display_width
= sys
->i_display_height
;
246 i_display_height
= sys
->i_display_width
;
247 aspect
= i_video_height
/ (double) i_video_width
;
249 i_display_width
= sys
->i_display_width
;
250 i_display_height
= sys
->i_display_height
;
251 aspect
= i_video_width
/ (double) i_video_height
;
254 if (i_display_width
/ aspect
< i_display_height
) {
255 i_width
= i_display_width
;
256 i_height
= i_display_width
/ aspect
;
258 i_width
= i_display_height
* aspect
;
259 i_height
= i_display_height
;
262 // Use the biggest size available
263 if (i_width
* i_height
< i_video_width
* i_video_height
) {
264 i_width
= i_video_width
;
265 i_height
= i_video_height
;
269 p_subfmt
->i_visible_width
= i_width
;
271 p_subfmt
->i_visible_height
= i_height
;
272 p_subfmt
->i_x_offset
= 0;
273 p_subfmt
->i_y_offset
= 0;
274 p_subfmt
->i_sar_num
= 1;
275 p_subfmt
->i_sar_den
= 1;
276 sys
->b_sub_invalid
= true;
279 #define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
280 static void SetupPictureYV12(picture_t
*p_picture
, uint32_t i_in_stride
)
282 /* according to document of android.graphics.ImageFormat.YV12 */
283 int i_stride
= ALIGN_16_PIXELS(i_in_stride
);
284 int i_c_stride
= ALIGN_16_PIXELS(i_stride
/ 2);
286 p_picture
->p
->i_pitch
= i_stride
;
288 /* Fill chroma planes for planar YUV */
289 for (int n
= 1; n
< p_picture
->i_planes
; n
++)
291 const plane_t
*o
= &p_picture
->p
[n
-1];
292 plane_t
*p
= &p_picture
->p
[n
];
294 p
->p_pixels
= o
->p_pixels
+ o
->i_lines
* o
->i_pitch
;
295 p
->i_pitch
= i_c_stride
;
296 p
->i_lines
= p_picture
->format
.i_height
/ 2;
298 Explicitly set the padding lines of the picture to black (127 for YUV)
299 since they might be used by Android during rescaling.
301 int visible_lines
= p_picture
->format
.i_visible_height
/ 2;
302 if (visible_lines
< p
->i_lines
)
303 memset(&p
->p_pixels
[visible_lines
* p
->i_pitch
], 127, (p
->i_lines
- visible_lines
) * p
->i_pitch
);
306 if (vlc_fourcc_AreUVPlanesSwapped(p_picture
->format
.i_chroma
,
308 picture_SwapUV( p_picture
);
311 static void AndroidWindow_DisconnectSurface(vout_display_sys_t
*sys
,
312 android_window
*p_window
)
314 if (p_window
->p_surface
) {
315 AWindowHandler_releaseANativeWindow(sys
->p_awh
, p_window
->id
);
316 p_window
->p_surface
= NULL
;
320 static int AndroidWindow_ConnectSurface(vout_display_sys_t
*sys
,
321 android_window
*p_window
)
323 if (!p_window
->p_surface
) {
324 p_window
->p_surface
= AWindowHandler_getANativeWindow(sys
->p_awh
,
326 if (!p_window
->p_surface
)
328 if (p_window
->b_opaque
)
329 p_window
->p_jsurface
= AWindowHandler_getSurface(sys
->p_awh
,
336 static android_window
*AndroidWindow_New(vout_display_t
*vd
,
337 const video_format_t
*p_fmt
,
340 vout_display_sys_t
*sys
= vd
->sys
;
341 android_window
*p_window
= NULL
;
343 p_window
= calloc(1, sizeof(android_window
));
348 p_window
->b_opaque
= p_fmt
->i_chroma
== VLC_CODEC_ANDROID_OPAQUE
;
349 if (!p_window
->b_opaque
) {
350 p_window
->i_android_hal
= ChromaToAndroidHal(p_fmt
->i_chroma
);
351 if (p_window
->i_android_hal
== -1)
355 switch (p_fmt
->orientation
)
357 case ORIENT_ROTATED_90
:
358 p_window
->i_angle
= 90;
360 case ORIENT_ROTATED_180
:
361 p_window
->i_angle
= 180;
363 case ORIENT_ROTATED_270
:
364 p_window
->i_angle
= 270;
367 p_window
->i_angle
= 0;
369 video_format_ApplyRotation(&p_window
->fmt
, p_fmt
);
370 p_window
->i_pic_count
= 1;
372 if (AndroidWindow_ConnectSurface(sys
, p_window
) != 0)
374 if (id
== AWindow_Video
)
375 msg_Err(vd
, "can't get Video Surface");
376 else if (id
== AWindow_Subtitles
)
377 msg_Err(vd
, "can't get Subtitles Surface");
387 static void AndroidWindow_Destroy(vout_display_t
*vd
,
388 android_window
*p_window
)
390 AndroidWindow_DisconnectSurface(vd
->sys
, p_window
);
394 static int AndroidWindow_SetupANW(vout_display_sys_t
*sys
,
395 android_window
*p_window
,
396 bool b_java_configured
)
398 p_window
->i_pic_count
= 1;
399 p_window
->i_min_undequeued
= 0;
401 if (!b_java_configured
&& sys
->anw
->setBuffersGeometry
)
402 return sys
->anw
->setBuffersGeometry(p_window
->p_surface
,
403 p_window
->fmt
.i_width
,
404 p_window
->fmt
.i_height
,
405 p_window
->i_android_hal
);
410 static int AndroidWindow_Setup(vout_display_sys_t
*sys
,
411 android_window
*p_window
,
412 unsigned int i_pic_count
)
414 bool b_java_configured
= false;
416 if (i_pic_count
!= 0)
417 p_window
->i_pic_count
= i_pic_count
;
419 if (!p_window
->b_opaque
) {
421 picture_t
*p_pic
= PictureAlloc(sys
, &p_window
->fmt
, false);
423 // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
424 align_pixels
= (16 / p_pic
->p
[0].i_pixel_pitch
) - 1;
425 p_window
->fmt
.i_height
= p_pic
->format
.i_height
;
426 p_window
->fmt
.i_width
= (p_pic
->format
.i_width
+ align_pixels
) & ~align_pixels
;
427 picture_Release(p_pic
);
429 if (AndroidWindow_SetupANW(sys
, p_window
, b_java_configured
) != 0)
432 sys
->p_window
->i_pic_count
= 31; // TODO
433 sys
->p_window
->i_min_undequeued
= 0;
439 static void AndroidWindow_UnlockPicture(vout_display_sys_t
*sys
,
440 android_window
*p_window
,
443 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
445 if (!p_picsys
->b_locked
)
448 sys
->anw
->unlockAndPost(p_window
->p_surface
);
450 p_picsys
->b_locked
= false;
453 static int AndroidWindow_LockPicture(vout_display_sys_t
*sys
,
454 android_window
*p_window
,
457 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
459 if (p_picsys
->b_locked
)
462 if (sys
->anw
->winLock(p_window
->p_surface
,
463 &p_picsys
->sw
.buf
, NULL
) != 0)
466 if (p_picsys
->sw
.buf
.width
< 0 ||
467 p_picsys
->sw
.buf
.height
< 0 ||
468 (unsigned)p_picsys
->sw
.buf
.width
< p_window
->fmt
.i_width
||
469 (unsigned)p_picsys
->sw
.buf
.height
< p_window
->fmt
.i_height
)
471 p_picsys
->b_locked
= true;
472 AndroidWindow_UnlockPicture(sys
, p_window
, p_pic
);
476 p_pic
->p
[0].p_pixels
= p_picsys
->sw
.buf
.bits
;
477 p_pic
->p
[0].i_lines
= p_picsys
->sw
.buf
.height
;
478 p_pic
->p
[0].i_pitch
= p_pic
->p
[0].i_pixel_pitch
* p_picsys
->sw
.buf
.stride
;
480 if (p_picsys
->sw
.buf
.format
== PRIV_WINDOW_FORMAT_YV12
)
481 SetupPictureYV12(p_pic
, p_picsys
->sw
.buf
.stride
);
483 p_picsys
->b_locked
= true;
487 static void SetRGBMask(video_format_t
*p_fmt
)
489 switch(p_fmt
->i_chroma
) {
490 case VLC_CODEC_RGB16
:
491 p_fmt
->i_bmask
= 0x0000001f;
492 p_fmt
->i_gmask
= 0x000007e0;
493 p_fmt
->i_rmask
= 0x0000f800;
496 case VLC_CODEC_RGB32
:
498 p_fmt
->i_rmask
= 0x000000ff;
499 p_fmt
->i_gmask
= 0x0000ff00;
500 p_fmt
->i_bmask
= 0x00ff0000;
505 static int OpenCommon(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
506 video_format_t
*fmtp
)
508 vout_display_sys_t
*sys
;
509 video_format_t fmt
, sub_fmt
;
511 vout_window_t
*embed
= cfg
->window
;
512 if (embed
->type
!= VOUT_WINDOW_TYPE_ANDROID_NATIVE
)
519 assert(embed
->handle
.anativewindow
);
520 AWindowHandler
*p_awh
= embed
->handle
.anativewindow
;
522 if (!AWindowHandler_canSetVideoLayout(p_awh
))
524 /* It's better to use gles2 if we are not able to change the video
529 /* Allocate structure */
530 vd
->sys
= sys
= (struct vout_display_sys_t
*)calloc(1, sizeof(*sys
));
536 sys
->anw
= AWindowHandler_getANativeWindowAPI(sys
->p_awh
);
538 sys
->i_display_width
= cfg
->display
.width
;
539 sys
->i_display_height
= cfg
->display
.height
;
541 if (fmt
.i_chroma
!= VLC_CODEC_ANDROID_OPAQUE
) {
543 char *psz_fcc
= var_InheritString(vd
, CFG_PREFIX
"chroma");
545 fmt
.i_chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, psz_fcc
);
548 fmt
.i_chroma
= VLC_CODEC_RGB32
;
550 switch(fmt
.i_chroma
) {
552 /* avoid swscale usage by asking for I420 instead since the
553 * vout already has code to swap the buffers */
554 fmt
.i_chroma
= VLC_CODEC_I420
;
557 case VLC_CODEC_RGB16
:
558 case VLC_CODEC_RGB32
:
561 video_format_FixRgb(&fmt
);
568 sys
->p_window
= AndroidWindow_New(vd
, &fmt
, AWindow_Video
);
572 if (AndroidWindow_Setup(sys
, sys
->p_window
, 0) != 0)
575 /* use software rotation if we don't do opaque */
576 if (!sys
->p_window
->b_opaque
)
577 video_format_TransformTo(&fmt
, ORIENT_NORMAL
);
579 msg_Dbg(vd
, "using %s", sys
->p_window
->b_opaque
? "opaque" : "ANW");
581 video_format_ApplyRotation(&sub_fmt
, &fmt
);
582 sub_fmt
.i_chroma
= subpicture_chromas
[0];
583 SetRGBMask(&sub_fmt
);
584 video_format_FixRgb(&sub_fmt
);
585 sys
->p_sub_window
= AndroidWindow_New(vd
, &sub_fmt
, AWindow_Subtitles
);
586 if (sys
->p_sub_window
) {
588 FixSubtitleFormat(sys
);
589 sys
->i_sub_last_order
= -1;
591 /* Export the subpicture capability of this vout. */
592 vd
->info
.subpicture_chromas
= subpicture_chromas
;
594 else if (!vd
->obj
.force
&& sys
->p_window
->b_opaque
)
596 msg_Warn(vd
, "cannot blend subtitles with an opaque surface, "
602 /* Setup vout_display */
604 vd
->prepare
= Prepare
;
605 vd
->display
= Display
;
606 vd
->control
= Control
;
607 vd
->info
.is_slow
= !sys
->p_window
->b_opaque
;
616 static int Open(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
617 video_format_t
*fmtp
, vlc_video_context
*context
)
619 if (fmtp
->i_chroma
== VLC_CODEC_ANDROID_OPAQUE
)
622 /* There are two cases:
623 * 1. the projection_mode is PROJECTION_MODE_RECTANGULAR
624 * 2. gles2 vout failed */
625 fmtp
->projection_mode
= PROJECTION_MODE_RECTANGULAR
;
628 return OpenCommon(vd
, cfg
, fmtp
);
631 static int OpenOpaque(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
632 video_format_t
*fmtp
, vlc_video_context
*context
)
634 if (fmtp
->i_chroma
!= VLC_CODEC_ANDROID_OPAQUE
)
638 && (fmtp
->projection_mode
!= PROJECTION_MODE_RECTANGULAR
639 || fmtp
->orientation
!= ORIENT_NORMAL
))
641 /* Let the gles2 vout handle orientation and projection */
646 return OpenCommon(vd
, cfg
, fmtp
);
649 static void ClearSurface(vout_display_t
*vd
)
651 vout_display_sys_t
*sys
= vd
->sys
;
653 if (sys
->p_window
->b_opaque
)
655 /* Clear the surface to black with OpenGL ES 2 */
656 char *modlist
= var_InheritString(sys
->embed
, "gles2");
657 vlc_gl_t
*gl
= vlc_gl_Create(sys
->embed
, VLC_OPENGL_ES2
, modlist
);
662 if (vlc_gl_MakeCurrent(gl
))
665 vlc_gl_Resize(gl
, 1, 1);
666 glClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);
667 glClear(GL_COLOR_BUFFER_BIT
);
670 vlc_gl_ReleaseCurrent(gl
);
677 android_window
*p_window
= sys
->p_window
;
678 ANativeWindow_Buffer buf
;
680 if (sys
->anw
->setBuffersGeometry(p_window
->p_surface
, 1, 1,
681 WINDOW_FORMAT_RGB_565
) == 0
682 && sys
->anw
->winLock(p_window
->p_surface
, &buf
, NULL
) == 0)
684 uint16_t *p_bit
= buf
.bits
;
686 sys
->anw
->unlockAndPost(p_window
->p_surface
);
691 static void Close(vout_display_t
*vd
)
693 vout_display_sys_t
*sys
= vd
->sys
;
695 /* Check if SPU regions have been properly cleared, and clear them if they
697 if (sys
->b_has_subpictures
)
699 SubpicturePrepare(vd
, NULL
);
700 AndroidWindow_UnlockPicture(sys
, sys
->p_sub_window
, sys
->p_sub_pic
);
704 picture_pool_Release(sys
->pool
);
708 if (sys
->b_displayed
)
710 AndroidWindow_Destroy(vd
, sys
->p_window
);
714 picture_Release(sys
->p_sub_pic
);
715 if (sys
->p_spu_blend
)
716 filter_DeleteBlend(sys
->p_spu_blend
);
717 free(sys
->p_sub_buffer_bounds
);
718 if (sys
->p_sub_window
)
719 AndroidWindow_Destroy(vd
, sys
->p_sub_window
);
722 AWindowHandler_setVideoLayout(sys
->p_awh
, 0, 0, 0, 0, 0, 0);
727 static int PoolLockPicture(picture_t
*p_pic
)
729 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
730 vout_display_sys_t
*sys
= p_picsys
->sw
.p_vd_sys
;
732 if (AndroidWindow_LockPicture(sys
, sys
->p_window
, p_pic
) != 0)
738 static void PoolUnlockPicture(picture_t
*p_pic
)
740 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
741 vout_display_sys_t
*sys
= p_picsys
->sw
.p_vd_sys
;
743 AndroidWindow_UnlockPicture(sys
, sys
->p_window
, p_pic
);
746 static int PoolLockOpaquePicture(picture_t
*p_pic
)
748 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
750 p_picsys
->b_locked
= true;
754 static void PoolUnlockOpaquePicture(picture_t
*p_pic
)
756 picture_sys_t
*p_picsys
= p_pic
->p_sys
;
758 AndroidOpaquePicture_Release(p_picsys
, false);
761 static picture_pool_t
*PoolAlloc(vout_display_t
*vd
, unsigned requested_count
)
763 vout_display_sys_t
*sys
= vd
->sys
;
764 picture_pool_t
*pool
= NULL
;
765 picture_t
**pp_pics
= NULL
;
768 msg_Dbg(vd
, "PoolAlloc: request %d frames", requested_count
);
769 if (AndroidWindow_Setup(sys
, sys
->p_window
, requested_count
) != 0)
772 requested_count
= sys
->p_window
->i_pic_count
;
773 msg_Dbg(vd
, "PoolAlloc: got %d frames", requested_count
);
775 UpdateVideoSize(sys
, &sys
->p_window
->fmt
);
777 pp_pics
= calloc(requested_count
, sizeof(picture_t
));
779 for (i
= 0; i
< requested_count
; i
++)
781 picture_t
*p_pic
= PictureAlloc(sys
, &sys
->p_window
->fmt
,
782 sys
->p_window
->b_opaque
);
789 picture_pool_configuration_t pool_cfg
;
790 memset(&pool_cfg
, 0, sizeof(pool_cfg
));
791 pool_cfg
.picture_count
= requested_count
;
792 pool_cfg
.picture
= pp_pics
;
793 if (sys
->p_window
->b_opaque
)
795 pool_cfg
.lock
= PoolLockOpaquePicture
;
796 pool_cfg
.unlock
= PoolUnlockOpaquePicture
;
800 pool_cfg
.lock
= PoolLockPicture
;
801 pool_cfg
.unlock
= PoolUnlockPicture
;
803 pool
= picture_pool_NewExtended(&pool_cfg
);
806 if (!pool
&& pp_pics
) {
807 for (unsigned j
= 0; j
< i
; j
++)
808 picture_Release(pp_pics
[j
]);
814 static void SubtitleRegionToBounds(subpicture_t
*subpicture
,
818 for (subpicture_region_t
*r
= subpicture
->p_region
; r
!= NULL
; r
= r
->p_next
) {
821 new_bounds
.left
= r
->i_x
;
822 new_bounds
.top
= r
->i_y
;
823 if (new_bounds
.left
< 0)
825 if (new_bounds
.top
< 0)
827 new_bounds
.right
= r
->fmt
.i_visible_width
+ r
->i_x
;
828 new_bounds
.bottom
= r
->fmt
.i_visible_height
+ r
->i_y
;
829 if (r
== &subpicture
->p_region
[0])
830 *p_out_bounds
= new_bounds
;
832 if (p_out_bounds
->left
> new_bounds
.left
)
833 p_out_bounds
->left
= new_bounds
.left
;
834 if (p_out_bounds
->right
< new_bounds
.right
)
835 p_out_bounds
->right
= new_bounds
.right
;
836 if (p_out_bounds
->top
> new_bounds
.top
)
837 p_out_bounds
->top
= new_bounds
.top
;
838 if (p_out_bounds
->bottom
< new_bounds
.bottom
)
839 p_out_bounds
->bottom
= new_bounds
.bottom
;
843 p_out_bounds
->left
= p_out_bounds
->top
= 0;
844 p_out_bounds
->right
= p_out_bounds
->bottom
= 0;
848 static void SubtitleGetDirtyBounds(vout_display_t
*vd
,
849 subpicture_t
*subpicture
,
852 vout_display_sys_t
*sys
= vd
->sys
;
854 bool b_found
= false;
856 /* Try to find last bounds set by current locked buffer.
857 * Indeed, even if we can lock only one buffer at a time, differents
858 * buffers can be locked. This functions will find the last bounds set by
859 * the current buffer. */
860 if (sys
->p_sub_buffer_bounds
) {
861 for (; sys
->p_sub_buffer_bounds
[i
].p_pixels
!= NULL
; ++i
) {
862 buffer_bounds
*p_bb
= &sys
->p_sub_buffer_bounds
[i
];
863 if (p_bb
->p_pixels
== sys
->p_sub_pic
->p
[0].p_pixels
) {
864 *p_out_bounds
= p_bb
->bounds
;
872 || p_out_bounds
->left
< 0
873 || p_out_bounds
->right
< 0
874 || (unsigned int) p_out_bounds
->right
> sys
->p_sub_pic
->format
.i_width
875 || p_out_bounds
->bottom
< 0
876 || p_out_bounds
->top
< 0
877 || (unsigned int) p_out_bounds
->top
> sys
->p_sub_pic
->format
.i_height
)
879 /* default is full picture */
880 p_out_bounds
->left
= 0;
881 p_out_bounds
->top
= 0;
882 p_out_bounds
->right
= sys
->p_sub_pic
->format
.i_width
;
883 p_out_bounds
->bottom
= sys
->p_sub_pic
->format
.i_height
;
886 /* buffer not found, add it to the array */
887 if (!sys
->p_sub_buffer_bounds
888 || sys
->p_sub_buffer_bounds
[i
].p_pixels
== NULL
) {
889 buffer_bounds
*p_bb
= realloc(sys
->p_sub_buffer_bounds
,
890 (i
+ 2) * sizeof(buffer_bounds
));
892 sys
->p_sub_buffer_bounds
= p_bb
;
893 sys
->p_sub_buffer_bounds
[i
].p_pixels
= sys
->p_sub_pic
->p
[0].p_pixels
;
894 sys
->p_sub_buffer_bounds
[i
+1].p_pixels
= NULL
;
898 /* set buffer bounds */
899 if (sys
->p_sub_buffer_bounds
900 && sys
->p_sub_buffer_bounds
[i
].p_pixels
!= NULL
)
901 SubtitleRegionToBounds(subpicture
, &sys
->p_sub_buffer_bounds
[i
].bounds
);
904 static void SubpicturePrepare(vout_display_t
*vd
, subpicture_t
*subpicture
)
906 vout_display_sys_t
*sys
= vd
->sys
;
909 SubtitleRegionToBounds(subpicture
, &memset_bounds
);
913 if( subpicture
->i_order
== sys
->i_sub_last_order
914 && memcmp( &memset_bounds
, &sys
->sub_last_region
, sizeof(ARect
) ) == 0 )
917 sys
->i_sub_last_order
= subpicture
->i_order
;
918 sys
->sub_last_region
= memset_bounds
;
921 if (AndroidWindow_LockPicture(sys
, sys
->p_sub_window
, sys
->p_sub_pic
) != 0)
924 /* Clear the subtitles surface. */
925 SubtitleGetDirtyBounds(vd
, subpicture
, &memset_bounds
);
926 const int x_pixels_offset
= memset_bounds
.left
927 * sys
->p_sub_pic
->p
[0].i_pixel_pitch
;
928 const int i_line_size
= (memset_bounds
.right
- memset_bounds
.left
)
929 * sys
->p_sub_pic
->p
->i_pixel_pitch
;
930 for (int y
= memset_bounds
.top
; y
< memset_bounds
.bottom
; y
++)
931 memset(&sys
->p_sub_pic
->p
[0].p_pixels
[y
* sys
->p_sub_pic
->p
[0].i_pitch
932 + x_pixels_offset
], 0, i_line_size
);
935 picture_BlendSubpicture(sys
->p_sub_pic
, sys
->p_spu_blend
, subpicture
);
938 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned requested_count
)
940 vout_display_sys_t
*sys
= vd
->sys
;
942 if (sys
->pool
== NULL
)
943 sys
->pool
= PoolAlloc(vd
, requested_count
);
947 static void Prepare(vout_display_t
*vd
, picture_t
*picture
,
948 subpicture_t
*subpicture
, vlc_tick_t date
)
950 vout_display_sys_t
*sys
= vd
->sys
;
953 if (subpicture
&& sys
->p_sub_window
) {
954 if (sys
->b_sub_invalid
) {
955 sys
->b_sub_invalid
= false;
956 if (sys
->p_sub_pic
) {
957 picture_Release(sys
->p_sub_pic
);
958 sys
->p_sub_pic
= NULL
;
960 if (sys
->p_spu_blend
) {
961 filter_DeleteBlend(sys
->p_spu_blend
);
962 sys
->p_spu_blend
= NULL
;
964 free(sys
->p_sub_buffer_bounds
);
965 sys
->p_sub_buffer_bounds
= NULL
;
969 && AndroidWindow_Setup(sys
, sys
->p_sub_window
, 1) == 0)
970 sys
->p_sub_pic
= PictureAlloc(sys
, &sys
->p_sub_window
->fmt
, false);
971 if (!sys
->p_spu_blend
&& sys
->p_sub_pic
)
972 sys
->p_spu_blend
= filter_NewBlend(VLC_OBJECT(vd
),
973 &sys
->p_sub_pic
->format
);
975 if (sys
->p_sub_pic
&& sys
->p_spu_blend
)
976 sys
->b_has_subpictures
= true;
978 /* As long as no subpicture was received, do not call
979 SubpictureDisplay since JNI calls and clearing the subtitles
980 surface are expensive operations. */
981 if (sys
->b_has_subpictures
)
983 SubpicturePrepare(vd
, subpicture
);
986 /* The surface has been cleared and there is no new
987 subpicture to upload, do not clear again until a new
988 subpicture is received. */
989 sys
->b_has_subpictures
= false;
992 if (sys
->p_window
->b_opaque
993 && AndroidOpaquePicture_CanReleaseAtTime(picture
->p_sys
))
995 vlc_tick_t now
= vlc_tick_now();
998 if (date
- now
<= VLC_TICK_FROM_SEC(1))
999 AndroidOpaquePicture_ReleaseAtTime(picture
->p_sys
, date
);
1000 else /* The picture will be displayed from the Display callback */
1001 msg_Warn(vd
, "picture way too early to release at time");
1006 static void Display(vout_display_t
*vd
, picture_t
*picture
)
1008 vout_display_sys_t
*sys
= vd
->sys
;
1009 VLC_UNUSED(picture
);
1011 if (sys
->p_window
->b_opaque
)
1012 AndroidOpaquePicture_Release(picture
->p_sys
, true);
1014 AndroidWindow_UnlockPicture(sys
, sys
->p_window
, picture
);
1017 AndroidWindow_UnlockPicture(sys
, sys
->p_sub_window
, sys
->p_sub_pic
);
1019 sys
->b_displayed
= true;
1022 static void CopySourceAspect(video_format_t
*p_dest
,
1023 const video_format_t
*p_src
)
1025 p_dest
->i_sar_num
= p_src
->i_sar_num
;
1026 p_dest
->i_sar_den
= p_src
->i_sar_den
;
1029 static int Control(vout_display_t
*vd
, int query
, va_list args
)
1031 vout_display_sys_t
*sys
= vd
->sys
;
1034 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
1035 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
:
1037 msg_Dbg(vd
, "change source crop/aspect");
1039 if (query
== VOUT_DISPLAY_CHANGE_SOURCE_CROP
) {
1040 video_format_CopyCrop(&sys
->p_window
->fmt
, &vd
->source
);
1042 CopySourceAspect(&sys
->p_window
->fmt
, &vd
->source
);
1044 UpdateVideoSize(sys
, &sys
->p_window
->fmt
);
1045 FixSubtitleFormat(sys
);
1048 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
:
1050 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
1052 sys
->i_display_width
= cfg
->display
.width
;
1053 sys
->i_display_height
= cfg
->display
.height
;
1054 msg_Dbg(vd
, "change display size: %dx%d", sys
->i_display_width
,
1055 sys
->i_display_height
);
1056 FixSubtitleFormat(sys
);
1059 case VOUT_DISPLAY_RESET_PICTURES
:
1060 vlc_assert_unreachable();
1062 msg_Warn(vd
, "Unknown request in android-display: %d", query
);
1063 case VOUT_DISPLAY_CHANGE_ZOOM
:
1064 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
1065 return VLC_EGENERIC
;