[vlc-commits] [Git][videolan/vlc][master] 13 commits: android: display: reorder to avoid forward declarations

Thomas Guillem (@tguillem) gitlab at videolan.org
Tue Mar 7 10:41:49 UTC 2023



Thomas Guillem pushed to branch master at VideoLAN / VLC


Commits:
65f1716f by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: reorder to avoid forward declarations

No functional changes.

- - - - -
46e666ce by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: remove the "android-display-chroma" option

- - - - -
37bd843d by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: remove unused define

- - - - -
a44e152f by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: disable software rendering

Software rendering is done via OpenGL.

Subtitles are still rendered via the native_window_api_t API.

- - - - -
eb0321af by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: remove unused p_jsurface

- - - - -
c612e9d2 by Thomas Guillem at 2023-03-07T10:27:37+00:00
Revert "vout/android: display: clear surface with OpenGL"

This reverts commit e66c9b7c6b7cd356df6cad1b5579e2e5d3ba905b and
158ab9157f1eef7258e2ad326b0ff1192c1ebdc8.

Fixes #26976

- - - - -
c81a0d74 by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: window: reorder code to avoid forward declaration

Also remove useless comments.

No functional changes.

- - - - -
e46dcf5b by Thomas Guillem at 2023-03-07T10:27:37+00:00
window: android: specify the surface id

This will allow to use EGL on the subtitle surface.

No functional changes.

- - - - -
e6b31327 by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: use new setVideoLayout API

That add the ability to update only size, crop or sar.

- - - - -
29a2a1e8 by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: rename p_awh

- - - - -
fea2930b by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: remove subtitle support

- - - - -
a77fb96f by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: add OpenGL subtitles support

- - - - -
f907ad15 by Thomas Guillem at 2023-03-07T10:27:37+00:00
android: display: only draw subpictures when needed

This avoid to acquire the GL context and swap. This can save 4-10ms when
not displaying any subtitles.

- - - - -


5 changed files:

- include/vlc_window.h
- modules/video_output/Makefile.am
- modules/video_output/android/display.c
- modules/video_output/android/window.c
- modules/video_output/opengl/egl.c


Changes:

=====================================
include/vlc_window.h
=====================================
@@ -394,7 +394,7 @@ typedef struct vlc_window {
         void     *hwnd;          /**< Win32 window handle */
         uint32_t xid;            /**< X11 windows ID */
         void     *nsobject;      /**< macOS/iOS view object */
-        void     *anativewindow; /**< Android native window */
+        int      android_id;     /**< AWindow_ID */
         struct wl_surface *wl;   /**< Wayland surface (client pointer) */
         void     *dcomp_visual;  /**<  Win32 direct composition visual */
         uint32_t crtc;           /**< KMS CRTC identifier */
@@ -413,6 +413,7 @@ typedef struct vlc_window {
         struct wl_display *wl; /**< Wayland display (client pointer) */
         void* dcomp_device; /**< DirectComposition device */
         int      drm_fd; /**< KMS DRM device */
+        void* anativewindow; /**< Android native window */
     } display;
 
     const struct vlc_window_operations *ops; /**< operations handled by the


=====================================
modules/video_output/Makefile.am
=====================================
@@ -304,7 +304,8 @@ libandroid_window_plugin_la_SOURCES = video_output/android/window.c \
 libandroid_window_plugin_la_LIBADD = libandroid_utils.la $(LIBDL)
 
 libandroid_display_plugin_la_SOURCES = video_output/android/display.c
-libandroid_display_plugin_la_LIBADD = libandroid_utils.la $(LIBDL)
+libandroid_display_plugin_la_CFLAGS = $(AM_CFLAGS) $(GLES2_CFLAGS) -DUSE_OPENGL_ES2
+libandroid_display_plugin_la_LIBADD = libvlc_opengles.la
 
 libglinterop_android_plugin_la_SOURCES = video_output/opengl/interop_android.c \
 	video_output/opengl/interop.h


=====================================
modules/video_output/android/display.c
=====================================
@@ -31,891 +31,429 @@
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_vout_display.h>
-#include <vlc_picture_pool.h>
-#include <vlc_filter.h>
-
-#include <vlc_opengl.h> /* for ClearSurface */
-#include <GLES2/gl2.h>  /* for ClearSurface */
-
-#include <dlfcn.h>
 
+#include <vlc_vector.h>
+#include <vlc_opengl.h>
 #include "utils.h"
+#include "../opengl/gl_api.h"
+#include "../opengl/sub_renderer.h"
 
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-#define USE_ANWP
-#define CHROMA_TEXT "Chroma used"
-#define CHROMA_LONGTEXT \
-    "Force use of a specific chroma for output. Default is RGB32."
-
-#define CFG_PREFIX "android-display-"
-static int Open(vout_display_t *vd,
-                video_format_t *fmtp, vlc_video_context *context);
-static void Close(vout_display_t *vd);
-static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture);
-
-vlc_module_begin()
-    set_subcategory(SUBCAT_VIDEO_VOUT)
-    set_description("Android video output")
-    add_shortcut("android-display")
-    add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT)
-    set_callback_display(Open, 260)
-vlc_module_end()
-
-/*****************************************************************************
- * Local prototypes
- *****************************************************************************/
-
-static const vlc_fourcc_t subpicture_chromas[] =
+struct sub_region
 {
-    VLC_CODEC_RGBA,
-    0
+    int x;
+    int y;
+    unsigned int width;
+    unsigned int height;
 };
 
-static void             Prepare(vout_display_t *, picture_t *, subpicture_t *, vlc_tick_t);
-static void             Display(vout_display_t *, picture_t *);
-static int              Control(vout_display_t *, int);
-
-typedef struct
+struct subpicture
 {
-    ANativeWindow_Buffer buf;
-    bool b_locked;
-} picture_sys_t;
-
-typedef struct android_window android_window;
-struct android_window
-{
-    video_format_t fmt;
-    int i_android_hal;
-    unsigned int i_angle;
-    bool b_opaque;
-
-    enum AWindow_ID id;
-    ANativeWindow *p_surface;
-    jobject       *p_jsurface;
+    vlc_window_t *window;
+    vlc_gl_t *gl;
+    struct vlc_gl_api api;
+    struct vlc_gl_interop *interop;
+    struct vlc_gl_sub_renderer *renderer;
+    vout_display_place_t place;
+    bool place_changed;
+    bool is_dirty;
+    bool clear;
+
+    int64_t last_order;
+    struct VLC_VECTOR(struct sub_region) regions;
+
+    struct {
+        PFNGLFLUSHPROC Flush;
+    } vt;
 };
 
-typedef struct buffer_bounds buffer_bounds;
-struct buffer_bounds
+struct sys
 {
-    uint8_t *p_pixels;
-    ARect bounds;
-};
-
-typedef struct vout_display_sys_t
-{
-    vlc_window_t *embed;
-
-    AWindowHandler *p_awh;
-    native_window_api_t *anw;
+    AWindowHandler *awh;
     android_video_context_t *avctx;
+    video_format_t fmt;
+    struct subpicture sub;
+};
 
-    android_window *p_window;
-    android_window *p_sub_window;
-
-    picture_t *p_prepared_pic; // local surface
-
-    bool b_displayed;
-    bool b_sub_invalid;
-    vlc_blender_t *p_spu_blend;
-    picture_t *p_sub_pic;
-    buffer_bounds *p_sub_buffer_bounds;
-    int64_t i_sub_last_order;
-    ARect sub_last_region;
-
-    bool b_has_subpictures;
-} vout_display_sys_t;
-
-#define PRIV_WINDOW_FORMAT_YV12 0x32315659
-
-static inline int ChromaToAndroidHal(vlc_fourcc_t i_chroma)
+static void FlipVerticalAlign(struct vout_display_placement *dp)
 {
-    switch (i_chroma) {
-        case VLC_CODEC_YV12:
-        case VLC_CODEC_I420:
-            return PRIV_WINDOW_FORMAT_YV12;
-        case VLC_CODEC_RGB16:
-            return WINDOW_FORMAT_RGB_565;
-        case VLC_CODEC_RGB32:
-            return WINDOW_FORMAT_RGBX_8888;
-        case VLC_CODEC_RGBA:
-            return WINDOW_FORMAT_RGBA_8888;
-        default:
-            return -1;
-    }
+    /* Reverse vertical alignment as the GL tex are Y inverted */
+    if (dp->align.vertical == VLC_VIDEO_ALIGN_TOP)
+        dp->align.vertical = VLC_VIDEO_ALIGN_BOTTOM;
+    else if (dp->align.vertical == VLC_VIDEO_ALIGN_BOTTOM)
+        dp->align.vertical = VLC_VIDEO_ALIGN_TOP;
 }
 
-static int UpdateVideoSize(vout_display_sys_t *sys, video_format_t *p_fmt)
+static int subpicture_Control(vout_display_t *vd, int query)
 {
-    unsigned int i_width, i_height;
-    unsigned int i_sar_num = 1, i_sar_den = 1;
-    video_format_t rot_fmt;
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-    video_format_ApplyRotation(&rot_fmt, p_fmt);
+    switch (query)
+    {
+    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+    case VOUT_DISPLAY_CHANGE_ZOOM:
+    {
+        struct vout_display_placement dp = vd->cfg->display;
 
-    if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0) {
-        i_sar_num = rot_fmt.i_sar_num;
-        i_sar_den = rot_fmt.i_sar_den;
+        FlipVerticalAlign(&dp);
+        vout_display_PlacePicture(&sub->place, vd->source, &dp);
+        sub->place_changed = true;
+        vlc_gl_Resize(sub->gl, dp.width, dp.height);
+        return VLC_SUCCESS;
     }
-    i_width = rot_fmt.i_width;
-    i_height = rot_fmt.i_height;
-
-    AWindowHandler_setVideoLayout(sys->p_awh, i_width, i_height,
-                                  rot_fmt.i_visible_width,
-                                  rot_fmt.i_visible_height,
-                                  i_sar_num, i_sar_den);
-    return 0;
-}
 
-static void AndroidPicture_Destroy(picture_t *pic)
-{
-    free(pic->p_sys);
+    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+        return VLC_SUCCESS;
+    default:
+        break;
+    }
+    return VLC_EGENERIC;
 }
 
-static picture_t *PictureAlloc(video_format_t *fmt)
+static bool subpicture_NeedDraw(vout_display_t *vd, subpicture_t *subpicture)
 {
-    picture_t *p_pic;
-    picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-    if (unlikely(p_picsys == NULL))
-        return NULL;
-
-    picture_resource_t rsc = {
-        .p_sys = p_picsys,
-        .pf_destroy = AndroidPicture_Destroy,
-    };
-
-    p_pic = picture_NewFromResource(fmt, &rsc);
-    if (!p_pic)
+    if (subpicture == NULL)
     {
-        free(p_picsys);
-        return NULL;
+        if (!sub->clear)
+            return false;
+        sub->clear = false;
+        /* Need to draw one last time in order to clear the current subpicture */
+        return true;
     }
-    return p_pic;
-}
 
-static void FixSubtitleFormat(vout_display_t *vd)
-{
-    vout_display_sys_t *sys = vd->sys;
-    video_format_t *p_subfmt;
-    video_format_t fmt;
-    int i_width, i_height;
-    int i_video_width, i_video_height;
-    int i_display_width, i_display_height;
-    double aspect;
-
-    if (!sys->p_sub_window)
-        return;
-    p_subfmt = &sys->p_sub_window->fmt;
+    sub->clear = true;
 
-    video_format_ApplyRotation(&fmt, &sys->p_window->fmt);
+    size_t count = 0;
+    for (subpicture_region_t *r = subpicture->p_region;
+         r != NULL; r = r->p_next)
+        count++;
 
-    if (fmt.i_visible_width == 0 || fmt.i_visible_height == 0) {
-        i_video_width = fmt.i_width;
-        i_video_height = fmt.i_height;
-    } else {
-        i_video_width = fmt.i_visible_width;
-        i_video_height = fmt.i_visible_height;
-    }
-
-    if (fmt.i_sar_num > 0 && fmt.i_sar_den > 0) {
-        if (fmt.i_sar_num >= fmt.i_sar_den)
-            i_video_width = i_video_width * fmt.i_sar_num / fmt.i_sar_den;
-        else
-            i_video_height = i_video_height * fmt.i_sar_den / fmt.i_sar_num;
+    if (subpicture->i_order != sub->last_order)
+    {
+        sub->last_order = subpicture->i_order;
+        /* Subpicture content is different */
+        goto end;
     }
 
-    if (sys->p_window->i_angle == 90 || sys->p_window->i_angle == 180) {
-        i_display_width = vd->cfg->display.height;
-        i_display_height = vd->cfg->display.width;
-        aspect = i_video_height / (double) i_video_width;
-    } else {
-        i_display_width = vd->cfg->display.width;
-        i_display_height = vd->cfg->display.height;
-        aspect = i_video_width / (double) i_video_height;
-    }
+    bool draw = false;
 
-    if (i_display_width / aspect < i_display_height) {
-        i_width = i_display_width;
-        i_height = i_display_width / aspect;
-    } else {
-        i_width = i_display_height * aspect;
-        i_height = i_display_height;
+    if (count == sub->regions.size)
+    {
+        size_t i = 0;
+        for (subpicture_region_t *r = subpicture->p_region;
+             r != NULL; r = r->p_next)
+        {
+            struct sub_region *cmp = &sub->regions.data[i++];
+            if (cmp->x != r->i_x || cmp->y != r->i_y
+             || cmp->width != r->fmt.i_visible_width
+             || cmp->height != r->fmt.i_visible_height)
+            {
+                /* Subpicture regions are different */
+                draw = true;
+                break;
+            }
+        }
     }
-
-    // Use the biggest size available
-    if (i_width * i_height < i_video_width * i_video_height) {
-        i_width = i_video_width;
-        i_height = i_video_height;
+    else
+    {
+        /* Subpicture region count is different */
+        draw = true;
     }
 
-    p_subfmt->i_width =
-    p_subfmt->i_visible_width = i_width;
-    p_subfmt->i_height =
-    p_subfmt->i_visible_height = i_height;
-    p_subfmt->i_x_offset = 0;
-    p_subfmt->i_y_offset = 0;
-    p_subfmt->i_sar_num = 1;
-    p_subfmt->i_sar_den = 1;
-    sys->b_sub_invalid = true;
-}
+    if (!draw)
+        return false;
 
-#define ALIGN_16_PIXELS( x ) ( ( ( x ) + 15 ) / 16 * 16 )
-static void SetupPictureYV12(picture_t *p_picture, uint32_t i_in_stride)
-{
-    /* according to document of android.graphics.ImageFormat.YV12 */
-    int i_stride = ALIGN_16_PIXELS(i_in_stride);
-    int i_c_stride = ALIGN_16_PIXELS(i_stride / 2);
+end:
+    /* Store the current subpicture regions in order to compare then later.
+     */
+    if (!vlc_vector_reserve(&sub->regions, count))
+        return false;
 
-    p_picture->p->i_pitch = i_stride;
+    sub->regions.size = 0;
 
-    /* Fill chroma planes for planar YUV */
-    for (int n = 1; n < p_picture->i_planes; n++)
+    for (subpicture_region_t *r = subpicture->p_region;
+         r != NULL; r = r->p_next)
     {
-        const plane_t *o = &p_picture->p[n-1];
-        plane_t *p = &p_picture->p[n];
-
-        p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
-        p->i_pitch  = i_c_stride;
-        p->i_lines  = p_picture->format.i_height / 2;
-        /*
-          Explicitly set the padding lines of the picture to black (127 for YUV)
-          since they might be used by Android during rescaling.
-        */
-        int visible_lines = p_picture->format.i_visible_height / 2;
-        if (visible_lines < p->i_lines)
-            memset(&p->p_pixels[visible_lines * p->i_pitch], 127, (p->i_lines - visible_lines) * p->i_pitch);
-    }
-
-    if (vlc_fourcc_AreUVPlanesSwapped(p_picture->format.i_chroma,
-                                      VLC_CODEC_YV12))
-        picture_SwapUV( p_picture );
+        struct sub_region reg = {
+            .x = r->i_x,
+            .y = r->i_y,
+            .width = r->fmt.i_visible_width,
+            .height = r->fmt.i_visible_height,
+        };
+        bool res = vlc_vector_push(&sub->regions, reg);
+        /* Already checked with vlc_vector_reserve */
+        assert(res); (void) res;
+    }
+
+    return true;
 }
 
-static void AndroidWindow_DisconnectSurface(vout_display_sys_t *sys,
-                                            android_window *p_window)
+static void subpicture_Prepare(vout_display_t *vd, subpicture_t *subpicture)
 {
-    if (p_window->p_surface) {
-        AWindowHandler_releaseANativeWindow(sys->p_awh, p_window->id);
-        p_window->p_surface = NULL;
-    }
-}
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-static int AndroidWindow_ConnectSurface(vout_display_sys_t *sys,
-                                        android_window *p_window)
-{
-    if (!p_window->p_surface) {
-        p_window->p_surface = AWindowHandler_getANativeWindow(sys->p_awh,
-                                                              p_window->id);
-        if (!p_window->p_surface)
-            return -1;
-        if (p_window->b_opaque)
-            p_window->p_jsurface = AWindowHandler_getSurface(sys->p_awh,
-                                                             p_window->id);
+    if (!subpicture_NeedDraw(vd, subpicture))
+    {
+        sub->is_dirty = false;
+        return;
     }
 
-    return 0;
-}
+    if (vlc_gl_MakeCurrent(sub->gl) != VLC_SUCCESS)
+        return;
 
-static android_window *AndroidWindow_New(vout_display_t *vd,
-                                         const video_format_t *p_fmt,
-                                         enum AWindow_ID id)
-{
-    vout_display_sys_t *sys = vd->sys;
-    android_window *p_window = NULL;
+    sub->api.vt.ClearColor(0.f, 0.f, 0.f, 0.f);
+    sub->api.vt.Clear(GL_COLOR_BUFFER_BIT);
 
-    p_window = calloc(1, sizeof(android_window));
-    if (!p_window)
+    int ret = vlc_gl_sub_renderer_Prepare(sub->renderer, subpicture);
+    if (ret != VLC_SUCCESS)
         goto error;
+    sub->vt.Flush();
 
-    p_window->id = id;
-    p_window->b_opaque = p_fmt->i_chroma == VLC_CODEC_ANDROID_OPAQUE;
-    if (!p_window->b_opaque) {
-        p_window->i_android_hal = ChromaToAndroidHal(p_fmt->i_chroma);
-        if (p_window->i_android_hal == -1)
-            goto error;
-    }
-
-    switch (p_fmt->orientation)
+    if (sub->place_changed)
     {
-        case ORIENT_ROTATED_90:
-            p_window->i_angle = 90;
-            break;
-        case ORIENT_ROTATED_180:
-            p_window->i_angle = 180;
-            break;
-        case ORIENT_ROTATED_270:
-            p_window->i_angle = 270;
-            break;
-        default:
-            p_window->i_angle = 0;
+        sub->api.vt.Viewport(sub->place.x, sub->place.y,
+                             sub->place.width, sub->place.height);
+        sub->place_changed = false;
     }
-    video_format_ApplyRotation(&p_window->fmt, p_fmt);
 
-    if (AndroidWindow_ConnectSurface(sys, p_window) != 0)
-    {
-        if (id == AWindow_Video)
-            msg_Err(vd, "can't get Video Surface");
-        else if (id == AWindow_Subtitles)
-            msg_Err(vd, "can't get Subtitles Surface");
+    ret = vlc_gl_sub_renderer_Draw(sub->renderer);
+    if (ret != VLC_SUCCESS)
         goto error;
-    }
+    sub->vt.Flush();
 
-    return p_window;
+    sub->is_dirty = true;
 error:
-    free(p_window);
-    return NULL;
-}
-
-static void AndroidWindow_Destroy(vout_display_t *vd,
-                                  android_window *p_window)
-{
-    AndroidWindow_DisconnectSurface(vd->sys, p_window);
-    free(p_window);
-}
-
-static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
-                                  android_window *p_window)
-{
-    if (!sys->anw->setBuffersGeometry)
-        return 0;
-    return sys->anw->setBuffersGeometry(p_window->p_surface,
-                                        p_window->fmt.i_width,
-                                        p_window->fmt.i_height,
-                                        p_window->i_android_hal);
-}
-
-static int AndroidWindow_SetupSW(vout_display_sys_t *sys,
-                                 android_window *p_window)
-{
-    assert(!p_window->b_opaque);
-
-    const vlc_chroma_description_t *p_dsc =
-        vlc_fourcc_GetChromaDescription( p_window->fmt.i_chroma );
-    if (p_dsc)
-    {
-        assert(p_dsc->pixel_size != 0);
-        // For RGB (32 or 16) we need to align on 8 or 4 pixels, 16 pixels for YUV
-        unsigned align_pixels = (16 / p_dsc->pixel_size) - 1;
-        p_window->fmt.i_width = (p_window->fmt.i_width + align_pixels) & ~align_pixels;
-    }
-
-    if (AndroidWindow_SetupANW(sys, p_window) != 0)
-        return -1;
-
-    return 0;
+    vlc_gl_ReleaseCurrent(sub->gl);
 }
 
-static void AndroidWindow_UnlockPicture(vout_display_sys_t *sys,
-                                        android_window *p_window,
-                                        picture_t *p_pic)
+static void subpicture_Display(vout_display_t *vd)
 {
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    if (!p_picsys->b_locked)
-        return;
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-    sys->anw->unlockAndPost(p_window->p_surface);
-
-    p_picsys->b_locked = false;
+    if (sub->is_dirty)
+        vlc_gl_Swap(sub->gl);
 }
 
-static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
-                                     android_window *p_window,
-                                     picture_t *p_pic)
+static void subpicture_CloseDisplay(vout_display_t *vd)
 {
-    picture_sys_t *p_picsys = p_pic->p_sys;
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-    if (p_picsys->b_locked)
-        return -1;
+    vlc_gl_MakeCurrent(sub->gl);
 
-    if (sys->anw->winLock(p_window->p_surface,
-                          &p_picsys->buf, NULL) != 0)
-        return -1;
+    vlc_gl_sub_renderer_Delete(sub->renderer);
+    vlc_gl_interop_Delete(sub->interop);
 
-    if (p_picsys->buf.width < 0 ||
-        p_picsys->buf.height < 0 ||
-        (unsigned)p_picsys->buf.width < p_window->fmt.i_width ||
-        (unsigned)p_picsys->buf.height < p_window->fmt.i_height)
-    {
-        p_picsys->b_locked = true;
-        AndroidWindow_UnlockPicture(sys, p_window, p_pic);
-        return -1;
-    }
+    vlc_gl_ReleaseCurrent(sub->gl);
 
-    p_pic->p[0].p_pixels = p_picsys->buf.bits;
-    p_pic->p[0].i_lines = p_picsys->buf.height;
-    p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->buf.stride;
+    vlc_gl_Delete(sub->gl);
 
-    if (p_picsys->buf.format == PRIV_WINDOW_FORMAT_YV12)
-        SetupPictureYV12(p_pic, p_picsys->buf.stride);
+    vlc_window_Disable(sub->window);
+    vlc_window_Delete(sub->window);
 
-    p_picsys->b_locked = true;
-    return 0;
+    vlc_vector_destroy(&sub->regions);
 }
 
-static void SetRGBMask(video_format_t *p_fmt)
+static void subpicture_window_Resized(struct vlc_window *wnd, unsigned width,
+                                    unsigned height, vlc_window_ack_cb cb,
+                                    void *opaque)
 {
-    switch(p_fmt->i_chroma) {
-        case VLC_CODEC_RGB16:
-            p_fmt->i_bmask = 0x0000001f;
-            p_fmt->i_gmask = 0x000007e0;
-            p_fmt->i_rmask = 0x0000f800;
-            break;
-
-        case VLC_CODEC_RGB32:
-        case VLC_CODEC_RGBA:
-            p_fmt->i_rmask = 0x000000ff;
-            p_fmt->i_gmask = 0x0000ff00;
-            p_fmt->i_bmask = 0x00ff0000;
-            break;
-    }
+    if (cb != NULL)
+        cb(wnd, width, height, opaque);
 }
 
-static const struct vlc_display_operations ops = {
-    .close = Close,
-    .prepare = Prepare,
-    .display = Display,
-    .control = Control,
-    .set_viewpoint = NULL,
-};
-
-static int Open(vout_display_t *vd,
-                video_format_t *fmtp, vlc_video_context *context)
+static int subpicture_window_Open(vlc_window_t *wnd)
 {
-    vout_display_sys_t *sys;
-    video_format_t fmt, sub_fmt;
-
-    vlc_window_t *embed = vd->cfg->window;
-    if (embed->type != VLC_WINDOW_TYPE_ANDROID_NATIVE)
-        return VLC_EGENERIC;
-
-    assert(embed->handle.anativewindow);
-    AWindowHandler *p_awh = embed->handle.anativewindow;
-
-    /* Allocate structure */
-    vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
-    if (!sys)
-        return VLC_ENOMEM;
-
-    sys->embed = embed;
-    sys->p_awh = p_awh;
-    sys->anw = AWindowHandler_getANativeWindowAPI(sys->p_awh);
-
-    fmt = *fmtp;
-    if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
-        /* Setup chroma */
-        char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
-        if (psz_fcc) {
-            fmt.i_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_fcc);
-            free(psz_fcc);
-        } else
-            fmt.i_chroma = VLC_CODEC_RGB32;
-
-        switch(fmt.i_chroma) {
-            case VLC_CODEC_YV12:
-                /* avoid swscale usage by asking for I420 instead since the
-                 * vout already has code to swap the buffers */
-                fmt.i_chroma = VLC_CODEC_I420;
-            case VLC_CODEC_I420:
-                break;
-            case VLC_CODEC_RGB16:
-            case VLC_CODEC_RGB32:
-            case VLC_CODEC_RGBA:
-                SetRGBMask(&fmt);
-                video_format_FixRgb(&fmt);
-                break;
-            default:
-                goto error;
-        }
-        sys->avctx = NULL;
-    }
-    else
-    {
-        if (!context)
-            goto error;
-        sys->avctx = vlc_video_context_GetPrivate(context, VLC_VIDEO_CONTEXT_AWINDOW);
-        assert(sys->avctx);
-        if (sys->avctx->texture != NULL)
-        {
-            /* video context configured for opengl */
-            goto error;
-        }
-    }
-
-    sys->p_window = AndroidWindow_New(vd, &fmt, AWindow_Video);
-    if (!sys->p_window)
-        goto error;
-
-    /* use software rotation if we don't do opaque */
-    if (!sys->p_window->b_opaque)
-        video_format_TransformTo(&fmt, ORIENT_NORMAL);
-
-    msg_Dbg(vd, "using %s", sys->p_window->b_opaque ? "opaque" : "ANW");
-
-    video_format_ApplyRotation(&sub_fmt, &fmt);
-    sub_fmt.i_chroma = subpicture_chromas[0];
-    SetRGBMask(&sub_fmt);
-    video_format_FixRgb(&sub_fmt);
-    sys->p_sub_window = AndroidWindow_New(vd, &sub_fmt, AWindow_Subtitles);
-    if (sys->p_sub_window) {
-
-        FixSubtitleFormat(vd);
-        sys->i_sub_last_order = -1;
-
-        /* Export the subpicture capability of this vout. */
-        vd->info.subpicture_chromas = subpicture_chromas;
-    }
-    else if (!vd->obj.force && sys->p_window->b_opaque)
-    {
-        msg_Warn(vd, "cannot blend subtitles with an opaque surface, "
-                     "trying next vout");
-        goto error;
-    }
-
-    /* Setup vout_display */
-    if (!sys->p_window->b_opaque)
-    {
-        if (AndroidWindow_SetupSW(sys, sys->p_window) != 0)
-            goto error;
-
-        sys->p_prepared_pic = PictureAlloc(&sys->p_window->fmt);
-        if (sys->p_prepared_pic == NULL)
-            goto error;
-
-        UpdateVideoSize(sys, &sys->p_window->fmt);
-    }
-
-    vd->ops = &ops;
-
-    *fmtp = fmt;
+    static const struct vlc_window_operations ops = {
+    };
 
+    wnd->type = VLC_WINDOW_TYPE_ANDROID_NATIVE;
+    wnd->display.anativewindow = wnd->owner.sys;
+    wnd->handle.android_id = AWindow_Subtitles;
+    wnd->ops = &ops;
     return VLC_SUCCESS;
-
-error:
-    Close(vd);
-    return VLC_EGENERIC;
 }
 
-static void ClearSurface(vout_display_t *vd)
+static int subpicture_OpenDisplay(vout_display_t *vd)
 {
-    vout_display_sys_t *sys = vd->sys;
+    struct sys *sys = vd->sys;
+    struct subpicture *sub = &sys->sub;
 
-    if (sys->p_window->b_opaque)
-    {
-        /* Clear the surface to black with OpenGL ES 2 */
-        char *modlist = var_InheritString(sys->embed, "gles2");
-        vlc_gl_t *gl = vlc_gl_Create(vd->cfg, VLC_OPENGL_ES2, modlist, NULL);
-        free(modlist);
-        if (gl == NULL)
-            return;
+    sub->is_dirty = false;
+    sub->clear = false;
+    sub->last_order = -1;
+    vlc_vector_init(&sub->regions);
 
-        if (vlc_gl_MakeCurrent(gl))
-            goto end;
-
-        vlc_gl_Resize(gl, 1, 1);
-        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-        glClear(GL_COLOR_BUFFER_BIT);
-        vlc_gl_Swap(gl);
-
-        vlc_gl_ReleaseCurrent(gl);
+    /* Create a VLC sub window that will hold the subpicture surface */
+    static const struct vlc_window_callbacks win_cbs = {
+        .resized = subpicture_window_Resized,
+    };
 
-end:
-        vlc_gl_Delete(gl);
-    }
-    else
-    {
-        android_window *p_window = sys->p_window;
-        ANativeWindow_Buffer buf;
+    const vlc_window_cfg_t win_cfg = {
+        .is_fullscreen = true,
+        .is_decorated = false,
+        .width = sys->fmt.i_width,
+        .height = sys->fmt.i_height,
+    };
 
-        if (sys->anw->setBuffersGeometry(p_window->p_surface, 1, 1,
-                                         WINDOW_FORMAT_RGB_565) == 0
-          && sys->anw->winLock(p_window->p_surface, &buf, NULL) == 0)
-        {
-            uint16_t *p_bit = buf.bits;
-            p_bit[0] = 0x0000;
-            sys->anw->unlockAndPost(p_window->p_surface);
-        }
-    }
-}
+    const vlc_window_owner_t win_owner = {
+        .cbs = &win_cbs,
+        .sys = vd->cfg->window->display.anativewindow,
+    };
 
-static void Close(vout_display_t *vd)
-{
-    vout_display_sys_t *sys = vd->sys;
+    sub->window = vlc_window_New(VLC_OBJECT(vd), "android-subpicture",
+                                 &win_owner, &win_cfg);
+    if (sub->window == NULL)
+        return -1;
 
-    /* Check if SPU regions have been properly cleared, and clear them if they
-     * were not. */
-    if (sys->b_has_subpictures)
-    {
-        SubpicturePrepare(vd, NULL);
-        AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic);
-    }
+    if (vlc_window_Enable(sub->window) != 0)
+        goto delete_win;
 
-    if (sys->p_window)
-    {
-        if (sys->b_displayed)
-            ClearSurface(vd);
-        AndroidWindow_Destroy(vd, sys->p_window);
-    }
+    /* Create the OpenGLES2 context on the subpicture window/surface */
+    vout_display_cfg_t vd_cfg = *vd->cfg;
+    vd_cfg.window = sub->window;
 
-    if (sys->p_prepared_pic)
-        picture_Release(sys->p_prepared_pic);
-    if (sys->p_sub_pic)
-        picture_Release(sys->p_sub_pic);
-    if (sys->p_spu_blend)
-        filter_DeleteBlend(sys->p_spu_blend);
-    free(sys->p_sub_buffer_bounds);
-    if (sys->p_sub_window)
-        AndroidWindow_Destroy(vd, sys->p_sub_window);
+    struct vlc_gl_cfg gl_cfg = { .need_alpha = true };
+    sub->gl = vlc_gl_Create(&vd_cfg, VLC_OPENGL_ES2, NULL, &gl_cfg);
+    if (sub->gl == NULL)
+        goto disable_win;
 
-    if (sys->embed)
-        AWindowHandler_setVideoLayout(sys->p_awh, 0, 0, 0, 0, 0, 0);
+    /* Initialize and configure subpicture renderer/interop */
+    struct vout_display_placement dp = vd->cfg->display;
+    FlipVerticalAlign(&dp);
+    vout_display_PlacePicture(&sub->place, vd->source, &dp);
+    sub->place_changed = true;
+    vlc_gl_Resize(sub->gl, dp.width, dp.height);
 
-    free(sys);
-}
+    if (vlc_gl_MakeCurrent(sub->gl))
+        goto delete_gl;
 
-static void SubtitleRegionToBounds(subpicture_t *subpicture,
-                                   ARect *p_out_bounds)
-{
-    if (subpicture) {
-        for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) {
-            ARect new_bounds;
-
-            new_bounds.left = r->i_x;
-            new_bounds.top = r->i_y;
-            if (new_bounds.left < 0)
-                new_bounds.left = 0;
-            if (new_bounds.top < 0)
-                new_bounds.top = 0;
-            new_bounds.right = r->fmt.i_visible_width + r->i_x;
-            new_bounds.bottom = r->fmt.i_visible_height + r->i_y;
-            if (r == &subpicture->p_region[0])
-                *p_out_bounds = new_bounds;
-            else {
-                if (p_out_bounds->left > new_bounds.left)
-                    p_out_bounds->left = new_bounds.left;
-                if (p_out_bounds->right < new_bounds.right)
-                    p_out_bounds->right = new_bounds.right;
-                if (p_out_bounds->top > new_bounds.top)
-                    p_out_bounds->top = new_bounds.top;
-                if (p_out_bounds->bottom < new_bounds.bottom)
-                    p_out_bounds->bottom = new_bounds.bottom;
-            }
-        }
-    } else {
-        p_out_bounds->left = p_out_bounds->top = 0;
-        p_out_bounds->right = p_out_bounds->bottom = 0;
-    }
-}
+    sub->vt.Flush = vlc_gl_GetProcAddress(sub->gl, "glFlush");
+    if (sub->vt.Flush == NULL)
+        goto release_gl;
 
-static void SubtitleGetDirtyBounds(vout_display_t *vd,
-                                   subpicture_t *subpicture,
-                                   ARect *p_out_bounds)
-{
-    vout_display_sys_t *sys = vd->sys;
-    int i = 0;
-    bool b_found = false;
-
-    /* Try to find last bounds set by current locked buffer.
-     * Indeed, even if we can lock only one buffer at a time, differents
-     * buffers can be locked. This functions will find the last bounds set by
-     * the current buffer. */
-    if (sys->p_sub_buffer_bounds) {
-        for (; sys->p_sub_buffer_bounds[i].p_pixels != NULL; ++i) {
-            buffer_bounds *p_bb = &sys->p_sub_buffer_bounds[i];
-            if (p_bb->p_pixels == sys->p_sub_pic->p[0].p_pixels) {
-                *p_out_bounds = p_bb->bounds;
-                b_found = true;
-                break;
-            }
-        }
-    }
+    int ret = vlc_gl_api_Init(&sub->api, sub->gl);
+    if (ret != VLC_SUCCESS)
+        goto release_gl;
 
-    if (!b_found
-     || p_out_bounds->left < 0
-     || p_out_bounds->right < 0
-     || (unsigned int) p_out_bounds->right > sys->p_sub_pic->format.i_width
-     || p_out_bounds->bottom < 0
-     || p_out_bounds->top < 0
-     || (unsigned int) p_out_bounds->top > sys->p_sub_pic->format.i_height)
+    sub->interop = vlc_gl_interop_NewForSubpictures(sub->gl);
+    if (sub->interop == NULL)
     {
-        /* default is full picture */
-        p_out_bounds->left = 0;
-        p_out_bounds->top = 0;
-        p_out_bounds->right = sys->p_sub_pic->format.i_width;
-        p_out_bounds->bottom = sys->p_sub_pic->format.i_height;
+        msg_Err(vd, "Could not create sub interop");
+        goto release_gl;
     }
 
-    /* buffer not found, add it to the array */
-    if (!sys->p_sub_buffer_bounds
-     || sys->p_sub_buffer_bounds[i].p_pixels == NULL) {
-        buffer_bounds *p_bb = realloc(sys->p_sub_buffer_bounds,
-                                      (i + 2) * sizeof(buffer_bounds));
-        if (p_bb) {
-            sys->p_sub_buffer_bounds = p_bb;
-            sys->p_sub_buffer_bounds[i].p_pixels = sys->p_sub_pic->p[0].p_pixels;
-            sys->p_sub_buffer_bounds[i+1].p_pixels = NULL;
-        }
-    }
+    sub->renderer = vlc_gl_sub_renderer_New(sub->gl, &sub->api, sub->interop);
+    if (sub->renderer == NULL)
+        goto delete_interop;
 
-    /* set buffer bounds */
-    if (sys->p_sub_buffer_bounds
-     && sys->p_sub_buffer_bounds[i].p_pixels != NULL)
-        SubtitleRegionToBounds(subpicture, &sys->p_sub_buffer_bounds[i].bounds);
-}
+    vlc_gl_ReleaseCurrent(sub->gl);
 
-static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture)
-{
-    vout_display_sys_t *sys = vd->sys;
-    ARect memset_bounds;
-
-    SubtitleRegionToBounds(subpicture, &memset_bounds);
-
-    if( subpicture )
-    {
-        if( subpicture->i_order == sys->i_sub_last_order
-         && memcmp( &memset_bounds, &sys->sub_last_region, sizeof(ARect) ) == 0 )
-            return;
-
-        sys->i_sub_last_order = subpicture->i_order;
-        sys->sub_last_region = memset_bounds;
-    }
+    static const vlc_fourcc_t gl_subpicture_chromas[] = {
+        VLC_CODEC_RGBA,
+        0
+    };
+    vd->info.subpicture_chromas = gl_subpicture_chromas;
 
-    if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0)
-        return;
+    return 0;
 
-    /* Clear the subtitles surface. */
-    SubtitleGetDirtyBounds(vd, subpicture, &memset_bounds);
-    const int x_pixels_offset = memset_bounds.left
-                                * sys->p_sub_pic->p[0].i_pixel_pitch;
-    const int i_line_size = (memset_bounds.right - memset_bounds.left)
-                            * sys->p_sub_pic->p->i_pixel_pitch;
-    for (int y = memset_bounds.top; y < memset_bounds.bottom; y++)
-        memset(&sys->p_sub_pic->p[0].p_pixels[y * sys->p_sub_pic->p[0].i_pitch
-                                              + x_pixels_offset], 0, i_line_size);
-
-    if (subpicture)
-        picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
+delete_interop:
+    vlc_gl_interop_Delete(sub->interop);
+release_gl:
+    vlc_gl_ReleaseCurrent(sub->gl);
+delete_gl:
+    vlc_gl_Delete(sub->gl);
+disable_win:
+    vlc_window_Disable(sub->window);
+delete_win:
+    vlc_window_Delete(sub->window);
+    sub->window = NULL;
+    return -1;
 }
 
 static void Prepare(vout_display_t *vd, picture_t *picture,
                     subpicture_t *subpicture, vlc_tick_t date)
 {
-    vout_display_sys_t *sys = vd->sys;
+    struct sys *sys = vd->sys;
 
-    if (sys->p_window->b_opaque)
+    assert(picture->context);
+    if (sys->avctx->render_ts != NULL)
     {
-        assert(picture->context);
-        if (sys->avctx->render_ts != NULL)
+        vlc_tick_t now = vlc_tick_now();
+        if (date > now)
         {
-            vlc_tick_t now = vlc_tick_now();
-            if (date > now)
-            {
-                if (date - now <= VLC_TICK_FROM_SEC(1))
-                    sys->avctx->render_ts(picture->context, date);
-                else /* The picture will be displayed from the Display callback */
-                    msg_Warn(vd, "picture way too early to release at time");
-            }
+            if (date - now <= VLC_TICK_FROM_SEC(1))
+                sys->avctx->render_ts(picture->context, date);
+            else /* The picture will be displayed from the Display callback */
+                msg_Warn(vd, "picture way too early to release at time");
         }
     }
-    else
-    {
-        if (AndroidWindow_LockPicture(sys, sys->p_window, sys->p_prepared_pic) == 0)
-        {
-            picture_Copy(sys->p_prepared_pic, picture);
-            AndroidWindow_UnlockPicture(sys, sys->p_window, sys->p_prepared_pic);
-        }
-    }
-
-    if (subpicture && sys->p_sub_window) {
-        if (sys->b_sub_invalid) {
-            sys->b_sub_invalid = false;
-            if (sys->p_sub_pic) {
-                picture_Release(sys->p_sub_pic);
-                sys->p_sub_pic = NULL;
-            }
-            if (sys->p_spu_blend) {
-                filter_DeleteBlend(sys->p_spu_blend);
-                sys->p_spu_blend = NULL;
-            }
-            free(sys->p_sub_buffer_bounds);
-            sys->p_sub_buffer_bounds = NULL;
-        }
-
-        if (!sys->p_sub_pic
-         && AndroidWindow_SetupSW(sys, sys->p_sub_window) == 0)
-            sys->p_sub_pic = PictureAlloc(&sys->p_sub_window->fmt);
-        if (!sys->p_spu_blend && sys->p_sub_pic)
-            sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd),
-                                               &sys->p_sub_pic->format);
 
-        if (sys->p_sub_pic && sys->p_spu_blend)
-            sys->b_has_subpictures = true;
-    }
-    /* As long as no subpicture was received, do not call
-       SubpictureDisplay since JNI calls and clearing the subtitles
-       surface are expensive operations. */
-    if (sys->b_has_subpictures)
-    {
-        SubpicturePrepare(vd, subpicture);
-        if (!subpicture)
-        {
-            /* The surface has been cleared and there is no new
-               subpicture to upload, do not clear again until a new
-               subpicture is received. */
-            sys->b_has_subpictures = false;
-        }
-    }
+    if (sys->sub.window != NULL)
+        subpicture_Prepare(vd, subpicture);
 }
 
 static void Display(vout_display_t *vd, picture_t *picture)
 {
-    vout_display_sys_t *sys = vd->sys;
-
-    if (sys->p_window->b_opaque)
-    {
-        assert(picture->context);
-        sys->avctx->render(picture->context);
-    }
-
-    if (sys->p_sub_pic)
-        AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic);
+    struct sys *sys = vd->sys;
+    assert(picture->context);
+    sys->avctx->render(picture->context);
 
-    sys->b_displayed = true;
-}
-
-static void CopySourceAspect(video_format_t *p_dest,
-                             const video_format_t *p_src)
-{
-    p_dest->i_sar_num = p_src->i_sar_num;
-    p_dest->i_sar_den = p_src->i_sar_den;
+    if (sys->sub.window != NULL)
+        subpicture_Display(vd);
 }
 
 static int Control(vout_display_t *vd, int query)
 {
-    vout_display_sys_t *sys = vd->sys;
+    struct sys *sys = vd->sys;
+
+    if (sys->sub.window != NULL)
+        subpicture_Control(vd, query);
 
     switch (query) {
     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+    {
+        msg_Dbg(vd, "change source crop: %ux%u @ %ux%u",
+                vd->source->i_x_offset, vd->source->i_y_offset,
+                vd->source->i_visible_width,
+                vd->source->i_visible_height);
+
+        video_format_CopyCrop(&sys->fmt, vd->source);
+
+        video_format_t rot_fmt;
+        video_format_ApplyRotation(&rot_fmt, &sys->fmt);
+        AWindowHandler_setVideoLayout(sys->awh, 0, 0,
+                                      rot_fmt.i_visible_width,
+                                      rot_fmt.i_visible_height,
+                                      0, 0);
+        return VLC_SUCCESS;
+    }
     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
     {
-        msg_Dbg(vd, "change source crop/aspect");
+        msg_Dbg(vd, "change source aspect: %d/%d", vd->source->i_sar_num,
+                vd->source->i_sar_den);
 
-        if (query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) {
-            video_format_CopyCrop(&sys->p_window->fmt, vd->source);
-        } else
-            CopySourceAspect(&sys->p_window->fmt, vd->source);
+        sys->fmt.i_sar_num = vd->source->i_sar_num;
+        sys->fmt.i_sar_den = vd->source->i_sar_den;
+        video_format_t rot_fmt;
+        video_format_ApplyRotation(&rot_fmt, &sys->fmt);
+        if (rot_fmt.i_sar_num != 0 && rot_fmt.i_sar_den != 0)
+            AWindowHandler_setVideoLayout(sys->awh, 0, 0, 0, 0,
+                                          rot_fmt.i_sar_num, rot_fmt.i_sar_den);
 
-        UpdateVideoSize(sys, &sys->p_window->fmt);
-        FixSubtitleFormat(vd);
         return VLC_SUCCESS;
     }
     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
     {
         msg_Dbg(vd, "change display size: %dx%d", vd->cfg->display.width,
                                                   vd->cfg->display.height);
-        FixSubtitleFormat(vd);
         return VLC_SUCCESS;
     }
     case VOUT_DISPLAY_CHANGE_ZOOM:
@@ -926,3 +464,83 @@ static int Control(vout_display_t *vd, int query)
         return VLC_EGENERIC;
     }
 }
+
+static void Close(vout_display_t *vd)
+{
+    struct sys *sys = vd->sys;
+
+    AWindowHandler_setVideoLayout(sys->awh, 0, 0, 0, 0, 0, 0);
+
+    if (sys->sub.window != NULL)
+        subpicture_CloseDisplay(vd);
+
+    free(sys);
+}
+
+static int Open(vout_display_t *vd,
+                video_format_t *fmtp, vlc_video_context *context)
+{
+    vlc_window_t *embed = vd->cfg->window;
+    if (embed->type != VLC_WINDOW_TYPE_ANDROID_NATIVE
+     || fmtp->i_chroma != VLC_CODEC_ANDROID_OPAQUE
+     || context == NULL)
+        return VLC_EGENERIC;
+
+    struct sys *sys;
+    vd->sys = sys = malloc(sizeof(*sys));
+    if (sys == NULL)
+        return VLC_ENOMEM;
+
+    video_format_ApplyRotation(&sys->fmt, fmtp);
+
+    sys->awh = embed->display.anativewindow;
+    sys->avctx = vlc_video_context_GetPrivate(context, VLC_VIDEO_CONTEXT_AWINDOW);
+    assert(sys->avctx);
+    if (sys->avctx->texture != NULL)
+    {
+        /* video context configured for opengl */
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    int ret = subpicture_OpenDisplay(vd);
+    if (ret != 0 && !vd->obj.force)
+    {
+        msg_Warn(vd, "cannot blend subtitle with an opaque surface, "
+                     "trying next vout");
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    video_format_t rot_fmt;
+    video_format_ApplyRotation(&rot_fmt, &sys->fmt);
+
+    AWindowHandler_setVideoLayout(sys->awh, rot_fmt.i_width, rot_fmt.i_height,
+                                  rot_fmt.i_visible_width,
+                                  rot_fmt.i_visible_height,
+                                  rot_fmt.i_sar_num, rot_fmt.i_sar_den);
+
+    static const struct vlc_display_operations ops = {
+        .close = Close,
+        .prepare = Prepare,
+        .display = Display,
+        .control = Control,
+        .set_viewpoint = NULL,
+    };
+
+    vd->ops = &ops;
+
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_description("Android video output")
+    add_shortcut("android-display")
+    add_obsolete_string("android-display-chroma") /* since 4.0.0 */
+    set_callback_display(Open, 260)
+    add_submodule ()
+        set_capability("vout window", 0)
+        set_callback(subpicture_window_Open)
+        add_shortcut("android-subpicture")
+vlc_module_end()


=====================================
modules/video_output/android/window.c
=====================================
@@ -39,24 +39,6 @@
 #include "utils.h"
 #include "../wasync_resize_compressor.h"
 
-static int Open(vlc_window_t *);
-static void Close(vlc_window_t *);
-static int OpenDecDevice(vlc_decoder_device *device, vlc_window_t *window);
-
-/*
- * Module descriptor
- */
-vlc_module_begin()
-    set_shortname(N_("Android Window"))
-    set_description(N_("Android native window"))
-    set_subcategory(SUBCAT_VIDEO_VOUT)
-    set_capability("vout window", 10)
-    set_callback(Open)
-    add_submodule ()
-        set_callback_dec_device(OpenDecDevice, 1)
-        add_shortcut("android")
-vlc_module_end()
-
 typedef struct
 {
     vlc_wasync_resize_compressor_t compressor;
@@ -86,15 +68,19 @@ static void OnNewMouseCoords(vlc_window_t *wnd,
     }
 }
 
-static const struct vlc_window_operations ops = {
-    .destroy = Close,
-};
+static void Close(vlc_window_t *wnd)
+{
+    vout_window_sys_t *sys = (vout_window_sys_t *) wnd->sys;
+    vlc_wasync_resize_compressor_destroy(&sys->compressor);
+    AWindowHandler_destroy(wnd->display.anativewindow);
+}
 
-/**
- * Create an Android native window.
- */
 static int Open(vlc_window_t *wnd)
 {
+    static const struct vlc_window_operations ops = {
+        .destroy = Close,
+    };
+
     /* We cannot create a window without the associated AWindow instance. */
     jobject jobj = var_InheritAddress(wnd, "drawable-androidwindow");
     if (jobj == NULL)
@@ -115,29 +101,19 @@ static int Open(vlc_window_t *wnd)
         return VLC_EGENERIC;
 
     wnd->type = VLC_WINDOW_TYPE_ANDROID_NATIVE;
-    wnd->handle.anativewindow = p_awh;
+    wnd->display.anativewindow = p_awh;
+    wnd->handle.android_id = AWindow_Video;
     wnd->ops = &ops;
 
     return VLC_SUCCESS;
 }
 
-
-/**
- * Destroys the Android native window.
- */
-static void Close(vlc_window_t *wnd)
-{
-    vout_window_sys_t *sys = (vout_window_sys_t *) wnd->sys;
-    vlc_wasync_resize_compressor_destroy(&sys->compressor);
-    AWindowHandler_destroy(wnd->handle.anativewindow);
-}
-
 static int
 OpenDecDevice(vlc_decoder_device *device, vlc_window_t *window)
 {
     AWindowHandler *awh;
     if (window && window->type == VLC_WINDOW_TYPE_ANDROID_NATIVE)
-        awh = window->handle.anativewindow;
+        awh = window->display.anativewindow;
     else
         awh = AWindowHandler_new(VLC_OBJECT(device), NULL, NULL);
 
@@ -151,3 +127,14 @@ OpenDecDevice(vlc_decoder_device *device, vlc_window_t *window)
 
     return VLC_SUCCESS;
 }
+
+vlc_module_begin()
+    set_shortname(N_("Android Window"))
+    set_description(N_("Android native window"))
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout window", 10)
+    set_callback(Open)
+    add_submodule ()
+        set_callback_dec_device(OpenDecDevice, 1)
+        add_shortcut("android")
+vlc_module_end()


=====================================
modules/video_output/opengl/egl.c
=====================================
@@ -472,16 +472,16 @@ out:
 
 static void DestroySurface(vlc_gl_t *gl)
 {
-    AWindowHandler_releaseANativeWindow(gl->surface->handle.anativewindow,
-                                        AWindow_Video);
+    AWindowHandler_releaseANativeWindow(gl->surface->display.anativewindow,
+                                        gl->surface->handle.android_id);
 }
 
 static EGLSurface CreateSurface(vlc_gl_t *gl, EGLDisplay dpy, EGLConfig config,
                                 unsigned int width, unsigned int height)
 {
     ANativeWindow *anw =
-        AWindowHandler_getANativeWindow(gl->surface->handle.anativewindow,
-                                        AWindow_Video);
+        AWindowHandler_getANativeWindow(gl->surface->display.anativewindow,
+                                        gl->surface->handle.android_id);
 
     (void) width; (void) height;
     return (anw != NULL) ? eglCreateWindowSurface(dpy, config, anw, NULL)



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/ed986711c159499b873157d4e5d4a53026d94719...f907ad15261c007895f3b720c84a4c68264fa20a

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/ed986711c159499b873157d4e5d4a53026d94719...f907ad15261c007895f3b720c84a4c68264fa20a
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list