[vlc-devel] [PATCH 4/5] mediacodec: use the new push model

Thomas Guillem thomas at gllm.fr
Thu Nov 21 15:30:51 CET 2019


pictures, context and video context are now allocated from the mediacodec
module. This simplify a lot of things since MediaCodec is naturally a push API.

The mediacodec decoder module, via the decoder device will now decide of the vd
plugin used: android-display or gles2. It depends on how the LibVLC user setup
its surfaces or if the format need an OpenGL transformation (360 videos). The
decoder module need to know this informations before configuring MediaCodec in
order to choose the good Surface. The Surface will be either the SurfaceView
directly (AWindow_Video) or a SurfaceTexture that will be attached to a GL
texture (AWindow_SurfaceTexture) by the android gl converter.
---
 include/vlc_picture.h                         |   2 +-
 modules/codec/omxil/mediacodec.c              | 365 ++++++++++++------
 modules/video_output/Makefile.am              |   1 -
 modules/video_output/android/display.c        | 302 ++++-----------
 modules/video_output/android/display.h        | 147 -------
 modules/video_output/android/utils.c          |  15 +
 modules/video_output/android/utils.h          |  10 +
 modules/video_output/android/window.c         |  22 ++
 .../video_output/opengl/converter_android.c   |  96 +----
 9 files changed, 395 insertions(+), 565 deletions(-)
 delete mode 100644 modules/video_output/android/display.h

diff --git a/include/vlc_picture.h b/include/vlc_picture.h
index 75a87c8a81a..b186adc1be9 100644
--- a/include/vlc_picture.h
+++ b/include/vlc_picture.h
@@ -96,7 +96,7 @@ enum vlc_video_context_type
     VLC_VIDEO_CONTEXT_VDPAU,
     VLC_VIDEO_CONTEXT_DXVA2, /**< private: d3d9_video_context_t* */
     VLC_VIDEO_CONTEXT_D3D11VA,  /**< private: d3d11_video_context_t* */
-    VLC_VIDEO_CONTEXT_AWINDOW,
+    VLC_VIDEO_CONTEXT_AWINDOW, /**< private: android_video_context_t* */
     VLC_VIDEO_CONTEXT_NVDEC,
     VLC_VIDEO_CONTEXT_CVPX,
     VLC_VIDEO_CONTEXT_MMAL,
diff --git a/modules/codec/omxil/mediacodec.c b/modules/codec/omxil/mediacodec.c
index 580225d1fbd..1d3de3bc5be 100644
--- a/modules/codec/omxil/mediacodec.c
+++ b/modules/codec/omxil/mediacodec.c
@@ -37,6 +37,7 @@
 #include <vlc_block_helper.h>
 #include <vlc_timestamp_helper.h>
 #include <vlc_threads.h>
+#include <vlc_atomic.h>
 #include <vlc_bits.h>
 
 #include "mediacodec.h"
@@ -44,12 +45,14 @@
 #include <OMX_Core.h>
 #include <OMX_Component.h>
 #include "omxil_utils.h"
-#include "../../video_output/android/display.h"
 
 #define BLOCK_FLAG_CSD (0x01 << BLOCK_FLAG_PRIVATE_SHIFT)
 
 #define DECODE_FLAG_RESTART (0x01)
 #define DECODE_FLAG_DRAIN (0x02)
+
+#define MAX_PIC 64
+
 /**
  * Callback called when a new block is processed from DecodeBlock.
  * It returns -1 in case of error, 0 if block should be dropped, 1 otherwise.
@@ -68,6 +71,13 @@ typedef void (*dec_on_flush_cb)(decoder_t *);
 typedef int (*dec_process_output_cb)(decoder_t *, mc_api_out *, picture_t **,
                                      block_t **);
 
+struct android_picture_ctx
+{
+    picture_context_t s;
+    atomic_uint refs;
+    atomic_int index;
+};
+
 typedef struct
 {
     mc_api api;
@@ -106,19 +116,19 @@ typedef struct
     bool            b_adaptive;
     int             i_decode_flags;
 
+    enum es_format_category_e cat;
     union
     {
         struct
         {
+            vlc_video_context *ctx;
+            struct android_picture_ctx apic_ctxs[MAX_PIC];
             void *p_surface, *p_jsurface;
             unsigned i_angle;
             unsigned i_input_width, i_input_height;
             unsigned int i_stride, i_slice_height;
             int i_pixel_format;
             struct hxxx_helper hh;
-            /* stores the inflight picture for each output buffer or NULL */
-            picture_sys_t** pp_inflight_pictures;
-            unsigned int i_inflight_pictures;
             timestamp_fifo_t *timestamp_fifo;
             int i_mpeg_dar_num, i_mpeg_dar_den;
         } video;
@@ -160,8 +170,7 @@ static void DecodeFlush(decoder_t *);
 static void StopMediaCodec(decoder_sys_t *);
 static void *OutThread(void *);
 
-static void InvalidateAllPictures(decoder_sys_t *);
-static void RemoveInflightPictures(decoder_sys_t *);
+static void ReleaseAllPictureContexts(decoder_sys_t *);
 
 /*****************************************************************************
  * Module descriptor
@@ -437,51 +446,6 @@ static int ParseExtra(decoder_t *p_dec)
         return VLC_SUCCESS;
 }
 
-static int UpdateVout(decoder_t *p_dec)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    if ((p_dec->fmt_in.i_codec == VLC_CODEC_MPGV ||
-         p_dec->fmt_in.i_codec == VLC_CODEC_MP2V) &&
-        (p_sys->video.i_mpeg_dar_num * p_sys->video.i_mpeg_dar_den != 0))
-    {
-        p_dec->fmt_out.video.i_sar_num =
-            p_sys->video.i_mpeg_dar_num * p_dec->fmt_out.video.i_height;
-        p_dec->fmt_out.video.i_sar_den =
-            p_sys->video.i_mpeg_dar_den * p_dec->fmt_out.video.i_width;
-    }
-
-    /* If MediaCodec can handle the rotation, reset the orientation to
-     * Normal in order to ask the vout not to rotate. */
-    if (p_sys->video.i_angle != 0)
-    {
-        assert(p_dec->fmt_out.i_codec == VLC_CODEC_ANDROID_OPAQUE);
-        p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
-        video_format_TransformTo(&p_dec->fmt_out.video, ORIENT_NORMAL);
-    }
-
-    if (decoder_UpdateVideoFormat(p_dec) != 0)
-        return VLC_EGENERIC;
-
-    if (p_dec->fmt_out.i_codec != VLC_CODEC_ANDROID_OPAQUE)
-        return VLC_SUCCESS;
-
-    /* Direct rendering: get the surface attached to the VOUT */
-    picture_t *p_dummy_hwpic = decoder_NewPicture(p_dec);
-    if (p_dummy_hwpic == NULL)
-        return VLC_EGENERIC;
-
-    picture_sys_t *p_picsys = p_dummy_hwpic->p_sys;
-    assert(p_picsys);
-    assert(p_picsys->hw.p_surface);
-    assert(p_picsys->hw.p_jsurface);
-
-    p_sys->video.p_surface = p_picsys->hw.p_surface;
-    p_sys->video.p_jsurface = p_picsys->hw.p_jsurface;
-    picture_Release(p_dummy_hwpic);
-    return VLC_SUCCESS;
-}
-
 /*****************************************************************************
  * StartMediaCodec: Create the mediacodec instance
  *****************************************************************************/
@@ -498,6 +462,7 @@ static int StartMediaCodec(decoder_t *p_dec)
 
         args.video.p_surface = p_sys->video.p_surface;
         args.video.p_jsurface = p_sys->video.p_jsurface;
+
         args.video.b_tunneled_playback = args.video.p_surface ?
                 var_InheritBool(p_dec, CFG_PREFIX "tunneled-playback") : false;
         if (p_sys->b_adaptive)
@@ -527,12 +492,214 @@ static void StopMediaCodec(decoder_sys_t *p_sys)
 {
     /* Remove all pictures that are currently in flight in order
      * to prevent the vout from using destroyed output buffers. */
-    if (p_sys->api.b_direct_rendering)
-        RemoveInflightPictures(p_sys);
+    if (p_sys->cat == VIDEO_ES)
+        ReleaseAllPictureContexts(p_sys);
 
     p_sys->api.stop(&p_sys->api);
 }
 
+static bool AndroidPictureContextRelease(struct android_picture_ctx *apctx,
+                                         bool render)
+{
+    int index = atomic_exchange(&apctx->index, -1);
+    if (index >= 0)
+    {
+        android_video_context_t *avctx =
+            vlc_video_context_GetPrivate(apctx->s.vctx, VLC_VIDEO_CONTEXT_AWINDOW);
+        decoder_sys_t *p_sys = avctx->dec_opaque;
+
+        p_sys->api.release_out(&p_sys->api, index, render);
+        return true;
+    }
+    return false;
+}
+
+static bool PictureContextRenderPic(struct picture_context_t *ctx)
+{
+    struct android_picture_ctx *apctx =
+        container_of(ctx, struct android_picture_ctx, s);
+
+    return AndroidPictureContextRelease(apctx, true);
+}
+
+static bool PictureContextRenderPicTs(struct picture_context_t *ctx,
+                                      vlc_tick_t ts)
+{
+    struct android_picture_ctx *apctx =
+        container_of(ctx, struct android_picture_ctx, s);
+
+    int index = atomic_exchange(&apctx->index, -1);
+    if (index >= 0)
+    {
+        android_video_context_t *avctx =
+            vlc_video_context_GetPrivate(ctx->vctx, VLC_VIDEO_CONTEXT_AWINDOW);
+        decoder_sys_t *p_sys = avctx->dec_opaque;
+
+        p_sys->api.release_out_ts(&p_sys->api, index, ts * INT64_C(1000));
+        return true;
+    }
+    return false;
+}
+
+static void PictureContextDestroy(struct picture_context_t *ctx)
+{
+    struct android_picture_ctx *apctx =
+        container_of(ctx, struct android_picture_ctx, s);
+
+    if (atomic_fetch_sub_explicit(&apctx->refs, 1, memory_order_acq_rel) == 1)
+        AndroidPictureContextRelease(apctx, false);
+}
+
+static struct picture_context_t *PictureContextCopy(struct picture_context_t *ctx)
+{
+    struct android_picture_ctx *apctx =
+        container_of(ctx, struct android_picture_ctx, s);
+
+    atomic_fetch_add_explicit(&apctx->refs, 1, memory_order_relaxed);
+    return ctx;
+}
+
+static void CleanFromVideoContext(void *priv)
+{
+    android_video_context_t *avctx = priv;
+    decoder_sys_t *p_sys = avctx->dec_opaque;
+
+    CleanDecoder(p_sys);
+}
+
+static void ReleaseAllPictureContexts(decoder_sys_t *p_sys)
+{
+    for (size_t i = 0; i < ARRAY_SIZE(p_sys->video.apic_ctxs); ++i)
+    {
+        struct android_picture_ctx *apctx = &p_sys->video.apic_ctxs[i];
+
+        /* Don't decrement apctx->refs, the picture_context should stay valid
+         * even if the underlying buffer is released since it might still be
+         * used by the vout (but the vout won't be able to render it). */
+        AndroidPictureContextRelease(apctx, false);
+    }
+}
+
+static struct android_picture_ctx *
+GetPictureContext(decoder_t *p_dec, unsigned index)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    bool slept = false;
+
+    for (;;)
+    {
+        for (size_t i = 0; i < ARRAY_SIZE(p_sys->video.apic_ctxs); ++i)
+        {
+            struct android_picture_ctx *apctx = &p_sys->video.apic_ctxs[i];
+            /* Find an available picture context (ie. refs == 1) */
+            unsigned expected_refs = 1;
+            if (atomic_compare_exchange_strong(&apctx->refs, &expected_refs, 2))
+            {
+                int expected_index = -1;
+                /* Store the new index */
+                if (likely(atomic_compare_exchange_strong(&apctx->index,
+                                                          &expected_index, index)))
+                    return apctx;
+
+                /* Unlikely: Restore the ref count and try a next one, since
+                 * this picture context is being released. Cf.
+                 * PictureContextDestroy(), this function first decrement the
+                 * ref count before releasing the index.  */
+                atomic_store(&apctx->refs, 1);
+            }
+        }
+
+        /* This is very unlikely since there are generally more picture
+         * contexts than android MediaCodec buffers */
+        if (!slept)
+            msg_Warn(p_dec, "waiting for more picture contexts (unlikely)");
+        vlc_tick_sleep(VOUT_OUTMEM_SLEEP);
+        slept = true;
+    }
+}
+
+static int
+CreateVideoContext(decoder_t *p_dec)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    /* If MediaCodec can handle the rotation, reset the orientation to
+     * Normal in order to ask the vout not to rotate. */
+    if (p_sys->video.i_angle != 0)
+    {
+        assert(p_dec->fmt_out.i_codec == VLC_CODEC_ANDROID_OPAQUE);
+        p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
+        video_format_TransformTo(&p_dec->fmt_out.video, ORIENT_NORMAL);
+    }
+
+    vlc_decoder_device *dec_dev = decoder_GetDecoderDevice(p_dec);
+    if (!dec_dev || dec_dev->type != VLC_DECODER_DEVICE_AWINDOW)
+    {
+        msg_Err(p_dec, "Could not find an AWINDOW decoder device");
+        return VLC_EGENERIC;
+    }
+
+    assert(dec_dev->opaque);
+    AWindowHandler *awh = dec_dev->opaque;
+    enum AWindow_ID id;
+
+    const bool has_subtitle_surface =
+        AWindowHandler_getANativeWindow(awh, AWindow_Subtitles) != NULL;
+
+    /* Force OpenGL interop (via AWindow_SurfaceTexture) if there is a
+     * projection or an orientation to handle, if the Surface owner is not able
+     * to modify its layout, or if there is no external subtitle surfaces. */
+
+    if (p_dec->fmt_out.video.projection_mode != PROJECTION_MODE_RECTANGULAR
+     || p_dec->fmt_out.video.orientation != ORIENT_NORMAL
+     || !AWindowHandler_canSetVideoLayout(awh)
+     || !has_subtitle_surface)
+        id = AWindow_SurfaceTexture;
+    else
+        id = AWindow_Video;
+
+    static const struct vlc_video_context_operations ops =
+    {
+        .destroy = CleanFromVideoContext,
+    };
+    p_sys->video.ctx =
+        vlc_video_context_Create(dec_dev, VLC_VIDEO_CONTEXT_AWINDOW,
+                                 sizeof(android_video_context_t), &ops);
+    vlc_decoder_device_Release(dec_dev);
+
+    if (!p_sys->video.ctx)
+        return VLC_EGENERIC;
+
+    p_sys->video.p_surface = AWindowHandler_getANativeWindow(awh, id);
+    p_sys->video.p_jsurface = AWindowHandler_getSurface(awh, id);
+    if (!p_sys->video.p_surface)
+    {
+        msg_Err(p_dec, "Could not find a valid ANativeWindow");
+        vlc_video_context_Release(p_sys->video.ctx);
+        p_sys->video.ctx = NULL;
+        return VLC_EGENERIC;
+    }
+
+    android_video_context_t *avctx =
+        vlc_video_context_GetPrivate(p_sys->video.ctx, VLC_VIDEO_CONTEXT_AWINDOW);
+    avctx->id = id;
+    avctx->dec_opaque = p_dec->p_sys;
+    avctx->render = PictureContextRenderPic;
+    avctx->render_ts = p_sys->api.release_out_ts ? PictureContextRenderPicTs : NULL;
+
+    for (size_t i = 0; i < ARRAY_SIZE(p_sys->video.apic_ctxs); ++i)
+    {
+        struct android_picture_ctx *apctx = &p_sys->video.apic_ctxs[i];
+
+        apctx->s.destroy = PictureContextDestroy;
+        apctx->s.copy = PictureContextCopy;
+        atomic_init(&apctx->index, -1);
+        atomic_init(&apctx->refs, 1);
+    }
+
+    return VLC_SUCCESS;
+}
+
 static void CleanInputVideo(decoder_t *p_dec)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -704,9 +871,6 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
         if (!p_sys->video.timestamp_fifo)
             goto bailout;
 
-        TAB_INIT(p_sys->video.i_inflight_pictures,
-                 p_sys->video.pp_inflight_pictures);
-
         if (var_InheritBool(p_dec, CFG_PREFIX "dr"))
         {
             /* Direct rendering: Request a valid OPAQUE Vout in order to get
@@ -746,12 +910,13 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
             p_sys->video.i_input_height =
             p_dec->fmt_out.video.i_visible_height = p_dec->fmt_out.video.i_height;
 
-            if (UpdateVout(p_dec) != VLC_SUCCESS)
+            if (CreateVideoContext(p_dec) != VLC_SUCCESS)
             {
-                msg_Err(p_dec, "Opaque Vout request failed");
+                msg_Err(p_dec, "video context creation failed");
                 goto bailout;
             }
         }
+        p_sys->cat = VIDEO_ES;
     }
     else
     {
@@ -768,6 +933,7 @@ static int OpenDecoder(vlc_object_t *p_this, pf_MediaCodecApi_init pf_init)
         }
 
         p_dec->fmt_out.audio = p_dec->fmt_in.audio;
+        p_sys->cat = AUDIO_ES;
     }
 
     /* Try first to configure CSD */
@@ -869,58 +1035,10 @@ static void CloseDecoder(vlc_object_t *p_this)
 
     CleanInputVideo(p_dec);
 
-    CleanDecoder(p_sys);
-}
-
-/*****************************************************************************
- * vout callbacks
- *****************************************************************************/
-static void ReleasePicture(decoder_t *p_dec, unsigned i_index, bool b_render)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    p_sys->api.release_out(&p_sys->api, i_index, b_render);
-}
-
-static void ReleasePictureTs(decoder_t *p_dec, unsigned i_index, vlc_tick_t i_ts)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-    assert(p_sys->api.release_out_ts);
-
-    p_sys->api.release_out_ts(&p_sys->api, i_index, i_ts * INT64_C(1000));
-}
-
-static void InvalidateAllPictures(decoder_sys_t *p_sys)
-{
-    for (unsigned int i = 0; i < p_sys->video.i_inflight_pictures; ++i)
-        AndroidOpaquePicture_Release(p_sys->video.pp_inflight_pictures[i],
-                                     false);
-}
-
-static int InsertInflightPicture(decoder_t *p_dec, picture_sys_t *p_picsys)
-{
-    decoder_sys_t *p_sys = p_dec->p_sys;
-
-    if (!p_picsys->hw.p_dec)
-    {
-        p_picsys->hw.p_dec = p_dec;
-        p_picsys->hw.pf_release = ReleasePicture;
-        if (p_sys->api.release_out_ts)
-            p_picsys->hw.pf_release_ts = ReleasePictureTs;
-        TAB_APPEND_CAST((picture_sys_t **),
-                        p_sys->video.i_inflight_pictures,
-                        p_sys->video.pp_inflight_pictures,
-                        p_picsys);
-    } /* else already attached */
-    return 0;
-}
-
-static void RemoveInflightPictures(decoder_sys_t *p_sys)
-{
-    for (unsigned int i = 0; i < p_sys->video.i_inflight_pictures; ++i)
-        AndroidOpaquePicture_DetachDecoder(p_sys->video.pp_inflight_pictures[i]);
-    TAB_CLEAN(p_sys->video.i_inflight_pictures,
-              p_sys->video.pp_inflight_pictures);
+    if (p_sys->video.ctx)
+        vlc_video_context_Release(p_sys->video.ctx);
+    else
+        CleanDecoder(p_sys);
 }
 
 static int Video_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
@@ -970,9 +1088,11 @@ static int Video_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
 
         if (p_sys->api.b_direct_rendering)
         {
-            picture_sys_t *p_picsys = p_pic->p_sys;
-            p_picsys->hw.i_index = p_out->buf.i_index;
-            InsertInflightPicture(p_dec, p_pic->p_sys);
+            struct android_picture_ctx *apctx =
+                GetPictureContext(p_dec,p_out->buf.i_index);
+            assert(apctx);
+            apctx->s.vctx = vlc_video_context_Hold(p_sys->video.ctx);
+            p_pic->context = &apctx->s;
         } else {
             unsigned int chroma_div;
             GetVlcChromaSizes(p_dec->fmt_out.i_codec,
@@ -1053,7 +1173,18 @@ static int Video_ProcessOutput(decoder_t *p_dec, mc_api_out *p_out,
             p_sys->video.i_stride = p_dec->fmt_out.video.i_width;
         }
 
-        if (UpdateVout(p_dec) != VLC_SUCCESS)
+
+        if ((p_dec->fmt_in.i_codec == VLC_CODEC_MPGV ||
+             p_dec->fmt_in.i_codec == VLC_CODEC_MP2V) &&
+            (p_sys->video.i_mpeg_dar_num * p_sys->video.i_mpeg_dar_den != 0))
+        {
+            p_dec->fmt_out.video.i_sar_num =
+                p_sys->video.i_mpeg_dar_num * p_dec->fmt_out.video.i_height;
+            p_dec->fmt_out.video.i_sar_den =
+                p_sys->video.i_mpeg_dar_den * p_dec->fmt_out.video.i_width;
+        }
+
+        if (decoder_UpdateVideoOutput(p_dec, p_sys->video.ctx) != 0)
         {
             msg_Err(p_dec, "UpdateVout failed");
             return -1;
@@ -1412,7 +1543,8 @@ static int QueueBlockLocked(decoder_t *p_dec, block_t *p_in_block,
             if (!b_dequeue_timeout)
             {
                 msg_Warn(p_dec, "Decoder stuck: invalidate all buffers");
-                InvalidateAllPictures(p_sys);
+                if (p_sys->cat == VIDEO_ES)
+                    ReleaseAllPictureContexts(p_sys);
                 b_dequeue_timeout = true;
                 continue;
             }
@@ -1683,8 +1815,7 @@ static void Video_OnFlush(decoder_t *p_dec)
     /* Invalidate all pictures that are currently in flight
      * since flushing make all previous indices returned by
      * MediaCodec invalid. */
-    if (p_sys->api.b_direct_rendering)
-        InvalidateAllPictures(p_sys);
+    ReleaseAllPictureContexts(p_sys);
 }
 
 static int Audio_OnNewBlock(decoder_t *p_dec, block_t **pp_block)
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 23a2bee5cbe..e14272fc158 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -274,7 +274,6 @@ libandroid_window_plugin_la_CFLAGS = $(AM_CFLAGS)
 libandroid_window_plugin_la_LIBADD = $(LIBDL)
 
 libandroid_display_plugin_la_SOURCES = video_output/android/display.c \
-	video_output/android/display.h \
 	video_output/android/utils.c video_output/android/utils.h
 libandroid_display_plugin_la_CFLAGS = $(AM_CFLAGS)
 libandroid_display_plugin_la_LIBADD = $(LIBDL)
diff --git a/modules/video_output/android/display.c b/modules/video_output/android/display.c
index e5c9553bcd6..547b269e835 100644
--- a/modules/video_output/android/display.c
+++ b/modules/video_output/android/display.c
@@ -39,7 +39,6 @@
 
 #include <dlfcn.h>
 
-#include "display.h"
 #include "utils.h"
 
 /*****************************************************************************
@@ -53,8 +52,6 @@
 #define CFG_PREFIX "android-display-"
 static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
                 video_format_t *fmtp, vlc_video_context *context);
-static int OpenOpaque(vout_display_t *vd, const vout_display_cfg_t *cfg,
-                      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);
 
@@ -65,10 +62,6 @@ vlc_module_begin()
     add_shortcut("android-display")
     add_string(CFG_PREFIX "chroma", NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
     set_callback_display(Open, 260)
-    add_submodule ()
-        set_description("Android opaque video output")
-        add_shortcut("android-opaque")
-        set_callback_display(OpenOpaque, 280)
 vlc_module_end()
 
 /*****************************************************************************
@@ -81,18 +74,22 @@ static const vlc_fourcc_t subpicture_chromas[] =
     0
 };
 
-static picture_pool_t   *Pool  (vout_display_t *, unsigned);
 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, va_list);
 
+typedef struct
+{
+    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;
-    unsigned int i_pic_count;
     bool b_opaque;
 
     enum AWindow_ID id;
@@ -117,6 +114,7 @@ struct vout_display_sys_t
 
     AWindowHandler *p_awh;
     native_window_api_t *anw;
+    android_video_context_t *avctx;
 
     android_window *p_window;
     android_window *p_sub_window;
@@ -180,34 +178,19 @@ static void AndroidPicture_Destroy(picture_t *pic)
     free(pic->p_sys);
 }
 
-static picture_t *PictureAlloc(vout_display_sys_t *sys, video_format_t *fmt,
-                               bool b_opaque)
+static picture_t *PictureAlloc(video_format_t *fmt)
 {
     picture_t *p_pic;
-    picture_resource_t rsc;
     picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
 
     if (unlikely(p_picsys == NULL))
         return NULL;
 
+    picture_resource_t rsc = {
+        .p_sys = p_picsys
+    };
 
-    memset(&rsc, 0, sizeof(picture_resource_t));
-    rsc.p_sys = p_picsys;
-
-    if (b_opaque)
-    {
-        p_picsys->hw.b_vd_ref = true;
-        p_picsys->hw.p_surface = sys->p_window->p_surface;
-        p_picsys->hw.p_jsurface =  sys->p_window->p_jsurface;
-        p_picsys->hw.i_index = -1;
-        vlc_mutex_init(&p_picsys->hw.lock);
-        rsc.pf_destroy = AndroidOpaquePicture_DetachVout;
-    }
-    else
-    {
-        p_picsys->sw.p_vd_sys = sys;
-        rsc.pf_destroy = AndroidPicture_Destroy;
-    }
+    rsc.pf_destroy = AndroidPicture_Destroy;
 
     p_pic = picture_NewFromResource(fmt, &rsc);
     if (!p_pic)
@@ -374,7 +357,6 @@ static android_window *AndroidWindow_New(vout_display_t *vd,
             p_window->i_angle = 0;
     }
     video_format_ApplyRotation(&p_window->fmt, p_fmt);
-    p_window->i_pic_count = 1;
 
     if (AndroidWindow_ConnectSurface(sys, p_window) != 0)
     {
@@ -401,8 +383,6 @@ static void AndroidWindow_Destroy(vout_display_t *vd,
 static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
                                   android_window *p_window)
 {
-    p_window->i_pic_count = 1;
-
     if (!sys->anw->setBuffersGeometry)
         return 0;
     return sys->anw->setBuffersGeometry(p_window->p_surface,
@@ -411,30 +391,24 @@ static int AndroidWindow_SetupANW(vout_display_sys_t *sys,
                                         p_window->i_android_hal);
 }
 
-static int AndroidWindow_Setup(vout_display_sys_t *sys,
-                               android_window *p_window,
-                               unsigned int i_pic_count)
+static int AndroidWindow_SetupSW(vout_display_sys_t *sys,
+                                 android_window *p_window)
 {
-    if (i_pic_count != 0)
-        p_window->i_pic_count = i_pic_count;
+    assert(!p_window->b_opaque);
 
-    if (!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;
-    } else {
-        sys->p_window->i_pic_count = 31; // TODO
+    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;
 }
 
@@ -462,25 +436,25 @@ static int AndroidWindow_LockPicture(vout_display_sys_t *sys,
         return -1;
 
     if (sys->anw->winLock(p_window->p_surface,
-                          &p_picsys->sw.buf, NULL) != 0)
+                          &p_picsys->buf, NULL) != 0)
         return -1;
 
-    if (p_picsys->sw.buf.width < 0 ||
-        p_picsys->sw.buf.height < 0 ||
-        (unsigned)p_picsys->sw.buf.width < p_window->fmt.i_width ||
-        (unsigned)p_picsys->sw.buf.height < p_window->fmt.i_height)
+    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;
     }
 
-    p_pic->p[0].p_pixels = p_picsys->sw.buf.bits;
-    p_pic->p[0].i_lines = p_picsys->sw.buf.height;
-    p_pic->p[0].i_pitch = p_pic->p[0].i_pixel_pitch * p_picsys->sw.buf.stride;
+    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;
 
-    if (p_picsys->sw.buf.format == PRIV_WINDOW_FORMAT_YV12)
-        SetupPictureYV12(p_pic, p_picsys->sw.buf.stride);
+    if (p_picsys->buf.format == PRIV_WINDOW_FORMAT_YV12)
+        SetupPictureYV12(p_pic, p_picsys->buf.stride);
 
     p_picsys->b_locked = true;
     return 0;
@@ -504,8 +478,8 @@ static void SetRGBMask(video_format_t *p_fmt)
     }
 }
 
-static int OpenCommon(vout_display_t *vd, const vout_display_cfg_t *cfg,
-                      video_format_t *fmtp)
+static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
+                video_format_t *fmtp, vlc_video_context *context)
 {
     vout_display_sys_t *sys;
     video_format_t fmt, sub_fmt;
@@ -514,20 +488,11 @@ static int OpenCommon(vout_display_t *vd, const vout_display_cfg_t *cfg,
     if (embed->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
         return VLC_EGENERIC;
 
-    fmt = *fmtp;
-
     if (embed == NULL)
         return VLC_EGENERIC;
     assert(embed->handle.anativewindow);
     AWindowHandler *p_awh = embed->handle.anativewindow;
 
-    if (!AWindowHandler_canSetVideoLayout(p_awh))
-    {
-        /* It's better to use gles2 if we are not able to change the video
-         * layout */
-        return VLC_EGENERIC;
-    }
-
     /* Allocate structure */
     vd->sys = sys = (struct vout_display_sys_t*)calloc(1, sizeof(*sys));
     if (!sys)
@@ -540,6 +505,7 @@ static int OpenCommon(vout_display_t *vd, const vout_display_cfg_t *cfg,
     sys->i_display_width = cfg->display.width;
     sys->i_display_height = cfg->display.height;
 
+    fmt = *fmtp;
     if (fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE) {
         /* Setup chroma */
         char *psz_fcc = var_InheritString(vd, CFG_PREFIX "chroma");
@@ -565,15 +531,25 @@ static int OpenCommon(vout_display_t *vd, const vout_display_cfg_t *cfg,
             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->id != AWindow_Video)
+        {
+            /* video context configured for opengl */
+            goto error;
+        }
     }
 
     sys->p_window = AndroidWindow_New(vd, &fmt, AWindow_Video);
     if (!sys->p_window)
         goto error;
 
-    if (AndroidWindow_Setup(sys, sys->p_window, 0) != 0)
-        goto error;
-
     /* use software rotation if we don't do opaque */
     if (!sys->p_window->b_opaque)
         video_format_TransformTo(&fmt, ORIENT_NORMAL);
@@ -601,20 +577,14 @@ static int OpenCommon(vout_display_t *vd, const vout_display_cfg_t *cfg,
     }
 
     /* Setup vout_display */
-    if (sys->p_window->b_opaque)
-        vd->pool    = Pool;
-    else
+    if (!sys->p_window->b_opaque)
     {
-        if (AndroidWindow_Setup(sys, sys->p_window, 1) != 0)
+        if (AndroidWindow_SetupSW(sys, sys->p_window) != 0)
             goto error;
 
-        sys->p_prepared_pic = PictureAlloc(sys, &sys->p_window->fmt, false);
+        sys->p_prepared_pic = PictureAlloc(&sys->p_window->fmt);
         if (sys->p_prepared_pic == NULL)
-        {
-            msg_Err(vd, "cannot allocate prepare surface");
             goto error;
-        }
-        msg_Dbg(vd, "PictureAlloc: got a frame");
 
         UpdateVideoSize(sys, &sys->p_window->fmt);
     }
@@ -633,39 +603,6 @@ error:
     return VLC_EGENERIC;
 }
 
-static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
-                video_format_t *fmtp, vlc_video_context *context)
-{
-    if (fmtp->i_chroma == VLC_CODEC_ANDROID_OPAQUE)
-        return VLC_EGENERIC;
-
-    /* There are two cases:
-     * 1. the projection_mode is PROJECTION_MODE_RECTANGULAR
-     * 2. gles2 vout failed */
-    fmtp->projection_mode = PROJECTION_MODE_RECTANGULAR;
-
-    (void) context;
-    return OpenCommon(vd, cfg, fmtp);
-}
-
-static int OpenOpaque(vout_display_t *vd, const vout_display_cfg_t *cfg,
-                      video_format_t *fmtp, vlc_video_context *context)
-{
-    if (fmtp->i_chroma != VLC_CODEC_ANDROID_OPAQUE)
-        return VLC_EGENERIC;
-
-    if (!vd->obj.force
-        && (fmtp->projection_mode != PROJECTION_MODE_RECTANGULAR
-            || fmtp->orientation != ORIENT_NORMAL))
-    {
-        /* Let the gles2 vout handle orientation and projection */
-        return VLC_EGENERIC;
-    }
-
-    (void) context;
-    return OpenCommon(vd, cfg, fmtp);
-}
-
 static void ClearSurface(vout_display_t *vd)
 {
     vout_display_sys_t *sys = vd->sys;
@@ -746,84 +683,6 @@ static void Close(vout_display_t *vd)
     free(sys);
 }
 
-static int PoolLockPicture(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-    vout_display_sys_t *sys = p_picsys->sw.p_vd_sys;
-
-    if (AndroidWindow_LockPicture(sys, sys->p_window, p_pic) != 0)
-        return -1;
-
-    return 0;
-}
-
-static void PoolUnlockPicture(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-    vout_display_sys_t *sys = p_picsys->sw.p_vd_sys;
-
-    AndroidWindow_UnlockPicture(sys, sys->p_window, p_pic);
-}
-
-static int PoolLockOpaquePicture(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    p_picsys->b_locked = true;
-    return 0;
-}
-
-static void PoolUnlockOpaquePicture(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    AndroidOpaquePicture_Release(p_picsys, false);
-}
-
-static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
-{
-    vout_display_sys_t *sys = vd->sys;
-    picture_pool_t *pool = NULL;
-    picture_t **pp_pics = NULL;
-    unsigned int i = 0;
-
-    msg_Dbg(vd, "PoolAlloc: request %d frames", requested_count);
-    if (AndroidWindow_Setup(sys, sys->p_window, requested_count) != 0)
-        goto error;
-
-    requested_count = sys->p_window->i_pic_count;
-    msg_Dbg(vd, "PoolAlloc: got %d frames", requested_count);
-
-    UpdateVideoSize(sys, &sys->p_window->fmt);
-
-    pp_pics = calloc(requested_count, sizeof(picture_t));
-
-    for (i = 0; i < requested_count; i++)
-    {
-        picture_t *p_pic = PictureAlloc(sys, &sys->p_window->fmt, true);
-        if (!p_pic)
-            goto error;
-
-        pp_pics[i] = p_pic;
-    }
-
-    picture_pool_configuration_t pool_cfg;
-    memset(&pool_cfg, 0, sizeof(pool_cfg));
-    pool_cfg.picture_count = requested_count;
-    pool_cfg.picture       = pp_pics;
-    pool_cfg.lock      = PoolLockOpaquePicture;
-    pool_cfg.unlock    = PoolUnlockOpaquePicture;
-    pool = picture_pool_NewExtended(&pool_cfg);
-
-error:
-    if (!pool && pp_pics) {
-        for (unsigned j = 0; j < i; j++)
-            picture_Release(pp_pics[j]);
-    }
-    free(pp_pics);
-    return pool;
-}
-
 static void SubtitleRegionToBounds(subpicture_t *subpicture,
                                    ARect *p_out_bounds)
 {
@@ -948,26 +807,32 @@ static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture)
         picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture);
 }
 
-static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
-{
-    vout_display_sys_t *sys = vd->sys;
-
-    if (sys->pool == NULL)
-        sys->pool = PoolAlloc(vd, requested_count);
-    return sys->pool;
-}
-
 static void Prepare(vout_display_t *vd, picture_t *picture,
                     subpicture_t *subpicture, vlc_tick_t date)
 {
     vout_display_sys_t *sys = vd->sys;
 
-    if (!sys->p_window->b_opaque)
+    if (sys->p_window->b_opaque)
     {
-        if (PoolLockPicture(sys->p_prepared_pic) == 0)
+        assert(picture->context);
+        if (sys->avctx->render_ts != NULL)
+        {
+            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");
+            }
+        }
+    }
+    else
+    {
+        if (AndroidWindow_LockPicture(sys, sys->p_window, sys->p_prepared_pic) == 0)
         {
             picture_Copy(sys->p_prepared_pic, picture);
-            PoolUnlockPicture(sys->p_prepared_pic);
+            AndroidWindow_UnlockPicture(sys, sys->p_window, sys->p_prepared_pic);
         }
     }
 
@@ -987,8 +852,8 @@ static void Prepare(vout_display_t *vd, picture_t *picture,
         }
 
         if (!sys->p_sub_pic
-         && AndroidWindow_Setup(sys, sys->p_sub_window, 1) == 0)
-            sys->p_sub_pic = PictureAlloc(sys, &sys->p_sub_window->fmt, false);
+         && 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);
@@ -1010,18 +875,6 @@ static void Prepare(vout_display_t *vd, picture_t *picture,
             sys->b_has_subpictures = false;
         }
     }
-    if (sys->p_window->b_opaque
-     && AndroidOpaquePicture_CanReleaseAtTime(picture->p_sys))
-    {
-        vlc_tick_t now = vlc_tick_now();
-        if (date > now)
-        {
-            if (date - now <= VLC_TICK_FROM_SEC(1))
-                AndroidOpaquePicture_ReleaseAtTime(picture->p_sys, date);
-            else /* The picture will be displayed from the Display callback */
-                msg_Warn(vd, "picture way too early to release at time");
-        }
-    }
 }
 
 static void Display(vout_display_t *vd, picture_t *picture)
@@ -1029,7 +882,10 @@ static void Display(vout_display_t *vd, picture_t *picture)
     vout_display_sys_t *sys = vd->sys;
 
     if (sys->p_window->b_opaque)
-        AndroidOpaquePicture_Release(picture->p_sys, true);
+    {
+        assert(picture->context);
+        sys->avctx->render(picture->context);
+    }
 
     if (sys->p_sub_pic)
         AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic);
diff --git a/modules/video_output/android/display.h b/modules/video_output/android/display.h
deleted file mode 100644
index 673de005435..00000000000
--- a/modules/video_output/android/display.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*****************************************************************************
- * display.h: Android video output module
- *****************************************************************************
- * Copyright (C) 2014 VLC authors and VideoLAN
- *
- * Authors: Thomas Guillem <thomas at gllm.fr>
- *          Felix Abecassis <felix.abecassis at gmail.com>
- *          Ming Hu <tewilove at gmail.com>
- *          Ludovic Fauvet <etix at l0cal.com>
- *          Sébastien Toque <xilasz at gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifndef ANDROID_WINDOW_H_
-#define ANDROID_WINDOW_H_
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <assert.h>
-#include <vlc_common.h>
-#include <vlc_vout_display.h>
-#include <android/native_window.h>
-
-typedef struct
-{
-    union {
-        struct {
-            void *p_surface;
-            void *p_jsurface;
-
-            vlc_mutex_t lock;
-            decoder_t *p_dec;
-            bool b_vd_ref;
-            int i_index;
-            void (*pf_release)(decoder_t *p_dec, unsigned int i_index,
-                               bool b_render);
-            void (*pf_release_ts)(decoder_t *p_dec, unsigned int i_index,
-                                  vlc_tick_t i_ts);
-        } hw;
-        struct {
-            vout_display_sys_t *p_vd_sys;
-            void *p_handle;
-            ANativeWindow_Buffer buf;
-        } sw;
-    };
-    bool b_locked;
-} picture_sys_t;
-
-static inline void
-AndroidOpaquePicture_DetachDecoder(picture_sys_t *p_picsys)
-{
-    vlc_mutex_lock(&p_picsys->hw.lock);
-    if (p_picsys->hw.i_index >= 0)
-    {
-        assert(p_picsys->hw.pf_release && p_picsys->hw.p_dec);
-        p_picsys->hw.pf_release(p_picsys->hw.p_dec,
-                                     (unsigned int) p_picsys->hw.i_index,
-                                     false);
-        p_picsys->hw.i_index = -1;
-    }
-    p_picsys->hw.pf_release = NULL;
-    p_picsys->hw.p_dec = NULL;
-    /* Release p_picsys if references from VOUT and from decoder are NULL */
-    if (!p_picsys->hw.b_vd_ref && !p_picsys->hw.p_dec)
-    {
-        vlc_mutex_unlock(&p_picsys->hw.lock);
-        vlc_mutex_destroy(&p_picsys->hw.lock);
-        free(p_picsys);
-    }
-    else
-        vlc_mutex_unlock(&p_picsys->hw.lock);
-}
-
-static inline void AndroidOpaquePicture_DetachVout(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    vlc_mutex_lock(&p_picsys->hw.lock);
-    p_picsys->hw.b_vd_ref = false;
-    /* Release p_picsys if references from VOUT and from decoder are NULL */
-    if (!p_picsys->hw.b_vd_ref && !p_picsys->hw.p_dec)
-    {
-        vlc_mutex_unlock(&p_picsys->hw.lock);
-        vlc_mutex_destroy(&p_picsys->hw.lock);
-        free(p_picsys);
-    }
-    else
-        vlc_mutex_unlock(&p_picsys->hw.lock);
-}
-
-static inline void
-AndroidOpaquePicture_Release(picture_sys_t *p_picsys, bool b_render)
-{
-    if (!p_picsys->b_locked)
-        return;
-    vlc_mutex_lock(&p_picsys->hw.lock);
-    if (p_picsys->hw.i_index >= 0)
-    {
-        assert(p_picsys->hw.pf_release && p_picsys->hw.p_dec);
-        p_picsys->hw.pf_release(p_picsys->hw.p_dec,
-                                (unsigned int) p_picsys->hw.i_index,
-                                b_render);
-        p_picsys->hw.i_index = -1;
-    }
-    vlc_mutex_unlock(&p_picsys->hw.lock);
-    p_picsys->b_locked = false;
-}
-
-static inline void
-AndroidOpaquePicture_ReleaseAtTime(picture_sys_t *p_picsys, vlc_tick_t i_ts)
-{
-    if (!p_picsys->b_locked)
-        return;
-    vlc_mutex_lock(&p_picsys->hw.lock);
-    if (p_picsys->hw.i_index >= 0)
-    {
-        assert(p_picsys->hw.pf_release_ts && p_picsys->hw.p_dec);
-        p_picsys->hw.pf_release_ts(p_picsys->hw.p_dec,
-                                   (unsigned int) p_picsys->hw.i_index, i_ts);
-        p_picsys->hw.i_index = -1;
-    }
-    vlc_mutex_unlock(&p_picsys->hw.lock);
-    p_picsys->b_locked = false;
-}
-
-static inline bool
-AndroidOpaquePicture_CanReleaseAtTime(picture_sys_t *p_picsys)
-{
-    return p_picsys->hw.pf_release_ts != NULL;
-}
-
-#endif
diff --git a/modules/video_output/android/utils.c b/modules/video_output/android/utils.c
index de65746f185..18a4cd425d9 100644
--- a/modules/video_output/android/utils.c
+++ b/modules/video_output/android/utils.c
@@ -537,6 +537,21 @@ AWindowHandler_new(vout_window_t *wnd, awh_events_t *p_events)
     p_awh->b_has_video_layout_listener =
         flags & AWINDOW_REGISTER_FLAGS_HAS_VIDEO_LAYOUT_LISTENER;
 
+    if (p_awh->b_has_video_layout_listener)
+    {
+        /* XXX: HACK: force mediacodec to setup an OpenGL surface when the vout
+         * is forced to gles2. Indeed, setting b_has_video_layout_listener to
+         * false will result in mediacodec using the AWindow_SurfaceTexture
+         * surface.
+         */
+        char *vout_modules = var_InheritString(wnd, "vout");
+        if (vout_modules
+         && (strncmp(vout_modules, "gles2", sizeof("gles2") - 1) == 0
+          || strncmp(vout_modules, "opengles2", sizeof("opengles2") - 1) == 0))
+            p_awh->b_has_video_layout_listener = false;
+        free(vout_modules);
+    }
+
     return p_awh;
 }
 
diff --git a/modules/video_output/android/utils.h b/modules/video_output/android/utils.h
index 5d61a309910..35ccc419065 100644
--- a/modules/video_output/android/utils.h
+++ b/modules/video_output/android/utils.h
@@ -68,6 +68,16 @@ typedef struct
                                 const struct awh_mouse_coords *coords);
 } awh_events_t;
 
+typedef struct android_video_context_t android_video_context_t;
+
+struct android_video_context_t
+{
+    enum AWindow_ID id;
+    void *dec_opaque;
+    bool (*render)(struct picture_context_t *ctx);
+    bool (*render_ts)(struct picture_context_t *ctx, vlc_tick_t ts);
+};
+
 /**
  * Attach or get a JNIEnv*
  *
diff --git a/modules/video_output/android/window.c b/modules/video_output/android/window.c
index 6cf1b94c07d..d8e4597426a 100644
--- a/modules/video_output/android/window.c
+++ b/modules/video_output/android/window.c
@@ -31,6 +31,7 @@
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_vout_window.h>
+#include <vlc_codec.h>
 
 #include <dlfcn.h>
 #include <jni.h>
@@ -39,6 +40,7 @@
 
 static int Open(vout_window_t *);
 static void Close(vout_window_t *);
+static int OpenDecDevice(vlc_decoder_device *device, vout_window_t *window);
 
 /*
  * Module descriptor
@@ -50,6 +52,9 @@ vlc_module_begin()
     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()
 
 
@@ -105,3 +110,20 @@ static void Close(vout_window_t *wnd)
 {
     AWindowHandler_destroy(wnd->handle.anativewindow);
 }
+
+static int
+OpenDecDevice(vlc_decoder_device *device, vout_window_t *window)
+{
+    if (!window || window->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
+        return VLC_EGENERIC;
+
+    static const struct vlc_decoder_device_operations ops = 
+    {
+        .close = NULL,
+    };
+    device->ops = &ops;
+    device->type = VLC_DECODER_DEVICE_AWINDOW;
+    device->opaque = window->handle.anativewindow;
+
+    return VLC_SUCCESS;
+}
diff --git a/modules/video_output/opengl/converter_android.c b/modules/video_output/opengl/converter_android.c
index 92ddd291fcb..9212f06f7a6 100644
--- a/modules/video_output/opengl/converter_android.c
+++ b/modules/video_output/opengl/converter_android.c
@@ -27,11 +27,11 @@
 #endif
 
 #include "converter.h"
-#include "../android/display.h"
 #include "../android/utils.h"
 
 struct priv
 {
+    android_video_context_t *avctx;
     AWindowHandler *awh;
     const float *transform_mtx;
     bool stex_attached;
@@ -41,23 +41,6 @@ struct priv
     } uloc;
 };
 
-static int
-pool_lock_pic(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    p_picsys->b_locked = true;
-    return 0;
-}
-
-static void
-pool_unlock_pic(picture_t *p_pic)
-{
-    picture_sys_t *p_picsys = p_pic->p_sys;
-
-    AndroidOpaquePicture_Release(p_picsys, false);
-}
-
 static int
 tc_anop_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
                           const GLsizei *tex_width, const GLsizei *tex_height)
@@ -74,75 +57,22 @@ tc_anop_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
     return VLC_SUCCESS;
 }
 
-static picture_pool_t *
-tc_anop_get_pool(const opengl_tex_converter_t *tc, unsigned requested_count)
-{
-    struct priv *priv = tc->priv;
-#define FORCED_COUNT 31
-    requested_count = FORCED_COUNT;
-    picture_t *picture[FORCED_COUNT] = {NULL, };
-    unsigned count;
-
-    for (count = 0; count < requested_count; count++)
-    {
-        picture_sys_t *p_picsys = calloc(1, sizeof(*p_picsys));
-        if (unlikely(p_picsys == NULL))
-            goto error;
-        picture_resource_t rsc = {
-            .p_sys = p_picsys,
-            .pf_destroy = AndroidOpaquePicture_DetachVout,
-        };
-
-        p_picsys->hw.b_vd_ref = true;
-        p_picsys->hw.p_surface = SurfaceTexture_getANativeWindow(priv->awh);
-        p_picsys->hw.p_jsurface = SurfaceTexture_getSurface(priv->awh);
-        p_picsys->hw.i_index = -1;
-        vlc_mutex_init(&p_picsys->hw.lock);
-
-        picture[count] = picture_NewFromResource(&tc->fmt, &rsc);
-        if (!picture[count])
-        {
-            free(p_picsys);
-            goto error;
-        }
-    }
-
-    /* Wrap the pictures into a pool */
-    picture_pool_configuration_t pool_cfg = {
-        .picture_count = requested_count,
-        .picture       = picture,
-        .lock          = pool_lock_pic,
-        .unlock        = pool_unlock_pic,
-    };
-    picture_pool_t *pool = picture_pool_NewExtended(&pool_cfg);
-    if (!pool)
-        goto error;
-
-    return pool;
-error:
-    for (unsigned i = 0; i < count; i++)
-        picture_Release(picture[i]);
-    return NULL;
-}
-
 static int
 tc_anop_update(const opengl_tex_converter_t *tc, GLuint *textures,
                const GLsizei *tex_width, const GLsizei *tex_height,
                picture_t *pic, const size_t *plane_offset)
 {
-    picture_sys_t *p_sys = pic->p_sys;
     (void) tex_width; (void) tex_height; (void) plane_offset;
+    assert(pic->context);
     assert(textures[0] != 0);
 
     if (plane_offset != NULL)
         return VLC_EGENERIC;
 
-    if (!p_sys->b_locked)
-        return VLC_SUCCESS;
-
     struct priv *priv = tc->priv;
 
-    AndroidOpaquePicture_Release(pic->p_sys, true);
+    if (!priv->avctx->render(pic->context))
+        return VLC_SUCCESS; /* already rendered */
 
     if (SurfaceTexture_waitAndUpdateTexImage(priv->awh, &priv->transform_mtx)
         != VLC_SUCCESS)
@@ -194,21 +124,35 @@ Open(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *) obj;
 
+    fprintf(stderr, "Open?\n");
     if (tc->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE
-     || !tc->gl->surface->handle.anativewindow)
+     || !tc->gl->surface->handle.anativewindow
+     || !tc->vctx)
+{
+fprintf(stderr, "Open: naup1\n");
         return VLC_EGENERIC;
+}
+
+    android_video_context_t *avctx =
+        vlc_video_context_GetPrivate(tc->vctx, VLC_VIDEO_CONTEXT_AWINDOW);
+
+    if (avctx->id != AWindow_SurfaceTexture)
+    {
+fprintf(stderr, "Open: naup1\n");
+        return VLC_EGENERIC;
+    }
 
     tc->priv = malloc(sizeof(struct priv));
     if (unlikely(tc->priv == NULL))
         return VLC_ENOMEM;
 
     struct priv *priv = tc->priv;
+    priv->avctx = avctx;
     priv->awh = tc->gl->surface->handle.anativewindow;
     priv->transform_mtx = NULL;
     priv->stex_attached = false;
 
     tc->pf_allocate_textures = tc_anop_allocate_textures;
-    tc->pf_get_pool       = tc_anop_get_pool;
     tc->pf_update         = tc_anop_update;
     tc->pf_fetch_locations = tc_anop_fetch_locations;
     tc->pf_prepare_shader = tc_anop_prepare_shader;
-- 
2.20.1



More information about the vlc-devel mailing list