[vlc-commits] [Git][videolan/vlc][master] 7 commits: fourcc: add GStreamer Memory opaque chroma

François Cartegnie (@fcartegnie) gitlab at videolan.org
Tue Dec 20 15:12:35 UTC 2022



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
5d6ce992 by Yann Lochet at 2022-12-20T15:00:25+00:00
fourcc: add GStreamer Memory opaque chroma

- - - - -
5d153322 by Yann Lochet at 2022-12-20T15:00:25+00:00
gstdecode: add format negotiation

This guarantees that the video sink gets a video chroma type supported
by VLC.

- - - - -
855ddd5c by Yann Lochet at 2022-12-20T15:00:25+00:00
gstdecode: fix potential memory leak

- - - - -
ecbf3940 by Yann Lochet at 2022-12-20T15:00:25+00:00
gstdecode: add support for DMABUF

- - - - -
c45a25e5 by Yann Lochet at 2022-12-20T15:00:25+00:00
opengl: add interop_gst_mem

This interop renders DMABUF-enabled GstBuffers via EGL,
EXT_image_dma_buf_import and GL_OES_EGL_image_external.

NV12 buffers are imported as a single EGLImage as there is no
guarantee that the alignements will be correct for a chrominance
EGLImage which dimensions are twice smaller.

- - - - -
30dd6d4c by Yann Lochet at 2022-12-20T15:00:25+00:00
gstdecode: add dummy video context and dummy decoder device

- - - - -
09f99896 by Yann Lochet at 2022-12-20T15:00:25+00:00
video_chroma: add converter for opaque format VLC_CODEC_GST_MEM_OPAQUE

- - - - -


18 changed files:

- configure.ac
- include/vlc_codec.h
- include/vlc_fourcc.h
- include/vlc_picture.h
- modules/codec/Makefile.am
- + modules/codec/gstreamer/gst_mem.h
- + modules/codec/gstreamer/gstcopypicture.c
- + modules/codec/gstreamer/gstcopypicture.h
- modules/codec/gstreamer/gstdecode.c
- modules/codec/gstreamer/gstvlcpictureplaneallocator.c
- modules/codec/gstreamer/gstvlcpictureplaneallocator.h
- modules/codec/gstreamer/gstvlcvideosink.c
- modules/codec/gstreamer/gstvlcvideosink.h
- modules/video_chroma/Makefile.am
- + modules/video_chroma/gst_mem.c
- modules/video_output/Makefile.am
- + modules/video_output/opengl/interop_gst_mem.c
- src/misc/fourcc.c


Changes:

=====================================
configure.ac
=====================================
@@ -2484,7 +2484,7 @@ AC_ARG_ENABLE([gst-decode],
 have_gst_decode="no"
 AS_IF([test "${enable_gst_decode}" != "no"], [
   PKG_CHECK_MODULES([GST_APP], [gstreamer-app-1.0], [
-    PKG_CHECK_MODULES([GST_VIDEO], [gstreamer-video-1.0], [
+    PKG_CHECK_MODULES([GST_VIDEO], [gstreamer-video-1.0 gstreamer-allocators-1.0], [
       have_gst_decode="yes"
     ], [
       AC_MSG_WARN([${GST_VIDEO_PKG_ERRORS}. GStreamer decoder module will not be built.])


=====================================
include/vlc_codec.h
=====================================
@@ -588,6 +588,7 @@ enum vlc_decoder_device_type
     VLC_DECODER_DEVICE_AWINDOW,
     VLC_DECODER_DEVICE_NVDEC,
     VLC_DECODER_DEVICE_MMAL,
+    VLC_DECODER_DEVICE_GSTDECODE,
 };
 
 struct vlc_decoder_device_operations


=====================================
include/vlc_fourcc.h
=====================================
@@ -439,6 +439,9 @@
 #define VLC_CODEC_CVPX_BGRA       VLC_FOURCC('C','V','P','B')
 #define VLC_CODEC_CVPX_P010       VLC_FOURCC('C','V','P','P')
 
+/* GStreamer Memory opaque buffer type */
+#define VLC_CODEC_GST_MEM_OPAQUE  VLC_FOURCC('G','S','T','M')
+
 /* Image codec (video) */
 #define VLC_CODEC_PNG             VLC_FOURCC('p','n','g',' ')
 #define VLC_CODEC_PPM             VLC_FOURCC('p','p','m',' ')


=====================================
include/vlc_picture.h
=====================================
@@ -98,6 +98,7 @@ enum vlc_video_context_type
     VLC_VIDEO_CONTEXT_NVDEC,     //!< empty
     VLC_VIDEO_CONTEXT_CVPX,      //!< private: cvpx_video_context*
     VLC_VIDEO_CONTEXT_MMAL,      //!< empty
+    VLC_VIDEO_CONTEXT_GSTDECODE, //!< empty
 };
 
 VLC_API vlc_video_context * vlc_video_context_Create(vlc_decoder_device *,


=====================================
modules/codec/Makefile.am
=====================================
@@ -614,6 +614,9 @@ libgstdecode_plugin_la_SOURCES = codec/gstreamer/gstdecode.c \
 								 codec/gstreamer/gstvlcvideopool.h \
 								 codec/gstreamer/gstvlcvideosink.c \
 								 codec/gstreamer/gstvlcvideosink.h \
+								 codec/gstreamer/gstcopypicture.c \
+								 codec/gstreamer/gstcopypicture.h \
+								 codec/gstreamer/gst_mem.h \
 								 codec/gstreamer/fourcc.c
 libgstdecode_plugin_la_CFLAGS = $(AM_CFLAGS) $(GST_VIDEO_CFLAGS) $(GST_APP_CFLAGS)
 libgstdecode_plugin_la_LIBADD = $(GST_VIDEO_LIBS) $(GST_APP_LIBS)


=====================================
modules/codec/gstreamer/gst_mem.h
=====================================
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * gst_mem.h: GStreamer Memory picture context for VLC
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Author: Yann Lochet <yann at l0chet.fr>
+ *
+ * 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 VLC_GST_MEM_H
+#define VLC_GST_MEM_H
+
+#include <vlc_picture.h>
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+struct gst_mem_pic_context
+{
+    picture_context_t s;
+    GstBuffer *p_buf;
+    GstVideoInfo *p_vinfo;
+};
+
+#endif


=====================================
modules/codec/gstreamer/gstcopypicture.c
=====================================
@@ -0,0 +1,61 @@
+/*****************************************************************************
+ * gstcopypicture.h: copy GStreamer frames into pictures
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Author: Yann Lochet <yann at l0chet.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+#include <vlc_picture.h>
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#include "gstcopypicture.h"
+
+/* Copy the frame data from the GstBuffer (from decoder)
+ * to the picture obtained from downstream in VLC.
+ * This function should be avoided as much
+ * as possible, since it involves a complete frame copy. */
+void gst_CopyPicture( picture_t *p_pic, GstVideoFrame *p_frame )
+{
+    int i_plane, i_planes, i_line, i_dst_stride, i_src_stride;
+    uint8_t *p_dst, *p_src;
+    int i_w, i_h;
+
+    i_planes = p_pic->i_planes;
+    for( i_plane = 0; i_plane < i_planes; i_plane++ )
+    {
+        p_dst = p_pic->p[i_plane].p_pixels;
+        p_src = GST_VIDEO_FRAME_PLANE_DATA( p_frame, i_plane );
+        i_dst_stride = p_pic->p[i_plane].i_pitch;
+        i_src_stride = GST_VIDEO_FRAME_PLANE_STRIDE( p_frame, i_plane );
+
+        i_w = GST_VIDEO_FRAME_COMP_WIDTH( p_frame,
+                i_plane ) * GST_VIDEO_FRAME_COMP_PSTRIDE( p_frame, i_plane );
+        i_h = GST_VIDEO_FRAME_COMP_HEIGHT( p_frame, i_plane );
+
+        for( i_line = 0;
+                i_line < __MIN( p_pic->p[i_plane].i_lines, i_h );
+                i_line++ )
+        {
+            memcpy( p_dst, p_src, i_w );
+            p_src += i_src_stride;
+            p_dst += i_dst_stride;
+        }
+    }
+}


=====================================
modules/codec/gstreamer/gstcopypicture.h
=====================================
@@ -0,0 +1,31 @@
+/*****************************************************************************
+ * gstcopypicture.h: copy GStreamer frames into pictures
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Author: Yann Lochet <yann at l0chet.fr>
+ *
+ * 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 VLC_GSTCOPYPICTURE_H
+#define VLC_GSTCOPYPICTURE_H
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+void gst_CopyPicture( picture_t *p_pic, GstVideoFrame *p_frame );
+
+#endif


=====================================
modules/codec/gstreamer/gstdecode.c
=====================================
@@ -34,12 +34,15 @@
 #include <gst/gst.h>
 #include <gst/video/video.h>
 #include <gst/video/gstvideometa.h>
+#include <gst/allocators/gstdmabuf.h>
 
 #include <gst/app/gstappsrc.h>
 #include <gst/gstatomicqueue.h>
 
 #include "gstvlcpictureplaneallocator.h"
 #include "gstvlcvideosink.h"
+#include "gstcopypicture.h"
+#include "gst_mem.h"
 
 typedef struct
 {
@@ -56,6 +59,8 @@ typedef struct
     GstAtomicQueue *p_que;
     bool b_prerolled;
     bool b_running;
+
+    vlc_video_context *vctx;
 } decoder_sys_t;
 
 typedef struct
@@ -71,6 +76,7 @@ static int  OpenDecoder( vlc_object_t* );
 static void CloseDecoder( vlc_object_t* );
 static int  DecodeBlock( decoder_t*, block_t* );
 static void Flush( decoder_t * );
+static int OpenDecoderDevice( vlc_decoder_device*, vlc_window_t* );
 
 #define MODULE_DESCRIPTION N_( "Uses GStreamer framework's plugins " \
         "to decode the media codecs" )
@@ -104,8 +110,37 @@ vlc_module_begin( )
         USEDECODEBIN_LONGTEXT )
     add_bool( "use-vlcpool", false, USEVLCPOOL_TEXT,
         USEVLCPOOL_LONGTEXT )
+    add_submodule( )
+        set_callback_dec_device( OpenDecoderDevice, 100 )
+        add_shortcut( "gstdecode" )
 vlc_module_end( )
 
+static void gst_mem_pic_context_Destroy( struct picture_context_t *ctx )
+{
+    struct gst_mem_pic_context *gst_mem_ctx = container_of( ctx,
+            struct gst_mem_pic_context, s );
+
+    gst_buffer_unref( gst_mem_ctx->p_buf );
+    free( gst_mem_ctx );
+}
+
+static picture_context_t *gst_mem_pic_context_Copy(
+        struct picture_context_t *ctx )
+{
+    struct gst_mem_pic_context *gst_mem_ctx = container_of( ctx,
+            struct gst_mem_pic_context, s );
+    struct gst_mem_pic_context *gst_mem_ctx_copy = calloc( 1,
+            sizeof( *gst_mem_ctx_copy ) );
+    if( unlikely( gst_mem_ctx_copy == NULL ) )
+        return NULL;
+
+    *gst_mem_ctx_copy = *gst_mem_ctx;
+    vlc_video_context_Hold( gst_mem_ctx_copy->s.vctx );
+    gst_buffer_ref( gst_mem_ctx_copy->p_buf );
+
+    return &gst_mem_ctx_copy->s;
+}
+
 void gst_vlc_dec_ensure_empty_queue( decoder_t *p_dec )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -148,6 +183,35 @@ static gboolean seek_data_cb( GstAppSrc *p_src, guint64 l_offset,
     return TRUE;
 }
 
+/* Emitted by decodebin when an autoplugged element not yet
+ * downstream-linked does a query.
+ * Used here for format and allocator negotiation. */
+static gboolean autoplug_query_cb( GstElement *p_bin, GstPad *p_pad,
+                                   GstElement *p_element, GstQuery *p_query,
+                                   gpointer p_data )
+{
+    VLC_UNUSED( p_bin );
+    decoder_t *p_dec = p_data;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( ( p_pad->direction == GST_PAD_SRC ) &&
+        GST_IS_VIDEO_DECODER( p_element ) )
+    {
+        switch( GST_QUERY_TYPE ( p_query ) ){
+        case GST_QUERY_CAPS:
+            return gst_vlc_video_sink_query_caps( p_query );
+        case GST_QUERY_ALLOCATION:
+            GstBaseSink *p_bsink = GST_BASE_SINK_CAST( p_sys->p_decode_out );
+            GstBaseSinkClass *p_bclass = GST_BASE_SINK_GET_CLASS( p_bsink );
+            return p_bclass->propose_allocation( p_bsink, p_query );
+        default:
+            return FALSE;
+        }
+    }
+
+    return FALSE;
+}
+
 /* Emitted by decodebin and links decodebin to vlcvideosink.
  * Since only one elementary codec stream is fed to decodebin,
  * this signal cannot be emitted more than once. */
@@ -215,39 +279,6 @@ static void frame_handoff_cb( GstElement *p_ele, GstBuffer *p_buf,
     gst_atomic_queue_push( p_sys->p_que, gst_buffer_ref( p_buf ) );
 }
 
-/* Copy the frame data from the GstBuffer (from decoder)
- * to the picture obtained from downstream in VLC.
- * This function should be avoided as much
- * as possible, since it involves a complete frame copy. */
-static void gst_CopyPicture( picture_t *p_pic, GstVideoFrame *p_frame )
-{
-    int i_plane, i_planes, i_line, i_dst_stride, i_src_stride;
-    uint8_t *p_dst, *p_src;
-    int i_w, i_h;
-
-    i_planes = p_pic->i_planes;
-    for( i_plane = 0; i_plane < i_planes; i_plane++ )
-    {
-        p_dst = p_pic->p[i_plane].p_pixels;
-        p_src = GST_VIDEO_FRAME_PLANE_DATA( p_frame, i_plane );
-        i_dst_stride = p_pic->p[i_plane].i_pitch;
-        i_src_stride = GST_VIDEO_FRAME_PLANE_STRIDE( p_frame, i_plane );
-
-        i_w = GST_VIDEO_FRAME_COMP_WIDTH( p_frame,
-                i_plane ) * GST_VIDEO_FRAME_COMP_PSTRIDE( p_frame, i_plane );
-        i_h = GST_VIDEO_FRAME_COMP_HEIGHT( p_frame, i_plane );
-
-        for( i_line = 0;
-                i_line < __MIN( p_pic->p[i_plane].i_lines, i_h );
-                i_line++ )
-        {
-            memcpy( p_dst, p_src, i_w );
-            p_src += i_src_stride;
-            p_dst += i_dst_stride;
-        }
-    }
-}
-
 /* Check if the element can use this caps */
 static gint find_decoder_func( gconstpointer p_p1, gconstpointer p_p2 )
 {
@@ -564,6 +595,8 @@ static int OpenDecoder( vlc_object_t *p_this )
         g_signal_connect( G_OBJECT( p_sys->p_decode_in ), "pad-added",
                 G_CALLBACK( pad_added_cb ), p_dec );
 
+        g_signal_connect( G_OBJECT( p_sys->p_decode_in ), "autoplug-query",
+                G_CALLBACK( autoplug_query_cb ), p_dec );
     }
 
     /* videosink: will emit signal for every available buffer */
@@ -623,6 +656,20 @@ static int OpenDecoder( vlc_object_t *p_this )
     p_dec->pf_decode = DecodeBlock;
     p_dec->pf_flush  = Flush;
 
+    vlc_decoder_device *dec_device = decoder_GetDecoderDevice( p_dec );
+    if( dec_device == NULL )
+    {
+        msg_Err( p_dec, "failed to get a decoder device" );
+        goto fail;
+    }
+    p_sys->vctx = vlc_video_context_Create( dec_device, VLC_VIDEO_CONTEXT_GSTDECODE, 0, NULL );
+    vlc_decoder_device_Release( dec_device );
+    if( unlikely( p_sys->vctx == NULL ) )
+    {
+        msg_Err( p_dec, "failed to create a video context" );
+        goto fail;
+    }
+
     return VLC_SUCCESS;
 
 fail:
@@ -775,22 +822,84 @@ static int DecodeBlock( decoder_t *p_dec, block_t *p_block )
         GstBuffer *p_buf = GST_BUFFER_CAST(
                 gst_atomic_queue_pop( p_sys->p_que ));
         GstMemory *p_mem;
+        p_mem = gst_buffer_peek_memory( p_buf, 0 );
 
-        if(( p_mem = gst_buffer_peek_memory( p_buf, 0 )) &&
+        bool b_copy_picture = true;
+
+        if( p_mem &&
             GST_IS_VLC_PICTURE_PLANE_ALLOCATOR( p_mem->allocator ))
         {
+            b_copy_picture = false;
             p_pic = picture_Hold(( (GstVlcPicturePlane*) p_mem )->p_pic );
         }
-        else
+        else if( p_mem && gst_is_dmabuf_memory(p_mem) )
+        {
+            b_copy_picture = false;
+
+            switch( p_dec->fmt_out.video.i_chroma ) {
+            case VLC_CODEC_NV12:
+                p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec =
+                    VLC_CODEC_GST_MEM_OPAQUE;
+                break;
+            case VLC_CODEC_GST_MEM_OPAQUE:
+                break;
+            /* fallback */
+            default:
+                b_copy_picture = true;
+            }
+
+            if( !b_copy_picture )
+            {
+                /* Get a new picture */
+                if( decoder_UpdateVideoOutput( p_dec, p_sys->vctx ) )
+                {
+                    gst_buffer_unref( p_buf );
+                    goto done;
+                }
+                p_pic = decoder_NewPicture( p_dec );
+                if( !p_pic )
+                {
+                    gst_buffer_unref( p_buf );
+                    goto done;
+                }
+
+                struct gst_mem_pic_context *pctx = calloc( 1, sizeof( *pctx ) );
+                if( unlikely( pctx == NULL ) )
+                {
+                    gst_buffer_unref( p_buf );
+                    return VLCDEC_ECRITICAL;
+                }
+
+                pctx->s = ( picture_context_t ) {
+                    gst_mem_pic_context_Destroy, gst_mem_pic_context_Copy,
+                    p_sys->vctx,
+                };
+                vlc_video_context_Hold( pctx->s.vctx );
+
+                pctx->p_buf = p_buf;
+                gst_buffer_ref( p_buf );
+
+                pctx->p_vinfo = &p_sys->vinfo;
+                p_pic->context = &pctx->s;
+            }
+        }
+
+        if( b_copy_picture )
         {
             GstVideoFrame frame;
 
             /* Get a new picture */
             if( decoder_UpdateVideoFormat( p_dec ) )
+            {
+                gst_buffer_unref( p_buf );
                 goto done;
+            }
             p_pic = decoder_NewPicture( p_dec );
             if( !p_pic )
+            {
+                gst_buffer_unref( p_buf );
                 goto done;
+            }
 
             if( unlikely( !gst_video_frame_map( &frame,
                             &p_sys->vinfo, p_buf, GST_MAP_READ ) ) )
@@ -804,6 +913,10 @@ static int DecodeBlock( decoder_t *p_dec, block_t *p_block )
             gst_video_frame_unmap( &frame );
         }
 
+        if( p_pic != NULL )
+            p_pic->b_progressive = ( p_sys->vinfo.interlace_mode ==
+                                     GST_VIDEO_INTERLACE_MODE_PROGRESSIVE );
+
         if( likely( GST_BUFFER_PTS_IS_VALID( p_buf ) ) )
             p_pic->date = gst_util_uint64_scale(
                 GST_BUFFER_PTS( p_buf ), GST_MSECOND, GST_SECOND );
@@ -892,5 +1005,22 @@ static void CloseDecoder( vlc_object_t *p_this )
     if( p_sys->p_decoder )
         gst_object_unref( p_sys->p_decoder );
 
+    if( p_sys->vctx )
+        vlc_video_context_Release( p_sys->vctx );
+
     free( p_sys );
 }
+
+static const struct vlc_decoder_device_operations gstdecode_device_ops = {
+    .close = NULL,
+};
+
+static int OpenDecoderDevice(vlc_decoder_device *device, vlc_window_t *window)
+{
+    VLC_UNUSED(window);
+
+    device->ops = &gstdecode_device_ops;
+    device->type = VLC_DECODER_DEVICE_GSTDECODE;
+
+    return VLC_SUCCESS;
+}


=====================================
modules/codec/gstreamer/gstvlcpictureplaneallocator.c
=====================================
@@ -132,7 +132,7 @@ static GstMemory* gst_vlc_picture_plane_copy(
     return NULL;
 }
 
-static vlc_fourcc_t gst_vlc_to_map_format( const char* psz_fourcc )
+vlc_fourcc_t gst_vlc_to_map_format( const char* psz_fourcc )
 {
     if( !psz_fourcc )
         return VLC_CODEC_UNKNOWN;


=====================================
modules/codec/gstreamer/gstvlcpictureplaneallocator.h
=====================================
@@ -79,6 +79,7 @@ bool gst_vlc_picture_plane_allocator_query_format(
         GstVideoAlignment *p_align, GstCaps *p_caps );
 bool gst_vlc_set_vout_fmt( GstVideoInfo *p_info, GstVideoAlignment *p_align,
         GstCaps *p_caps, decoder_t *p_dec );
+vlc_fourcc_t gst_vlc_to_map_format( const char* psz_fourcc );
 void gst_vlc_dec_ensure_empty_queue( decoder_t* p_dec );
 bool gst_vlc_picture_plane_allocator_hold( GstVlcPicturePlaneAllocator
         *p_allocator, GstBuffer *p_buffer );


=====================================
modules/codec/gstreamer/gstvlcvideosink.c
=====================================
@@ -25,6 +25,7 @@
  *****************************************************************************/
 #include "gstvlcvideopool.h"
 #include "gstvlcvideosink.h"
+#include "gstvlcpictureplaneallocator.h"
 
 #include <vlc_common.h>
 
@@ -57,7 +58,9 @@ static GstStaticPadTemplate sink_template =
 static gboolean gst_vlc_video_sink_setcaps( GstBaseSink *p_bsink,
         GstCaps *p_caps );
 static gboolean gst_vlc_video_sink_propose_allocation( GstBaseSink *p_bsink,
-        GstQuery *p_query);
+        GstQuery *p_query );
+static gboolean gst_vlc_video_sink_query( GstBaseSink *p_bsink,
+        GstQuery *p_query );
 static GstFlowReturn gst_vlc_video_sink_chain( GstBaseSink *p_vsink,
         GstBuffer *p_buffer );
 
@@ -127,6 +130,8 @@ static void gst_vlc_video_sink_class_init( GstVlcVideoSinkClass *p_klass )
     p_gstbasesink_class->propose_allocation =
         gst_vlc_video_sink_propose_allocation;
 
+    p_gstbasesink_class->query = gst_vlc_video_sink_query;
+
     p_gstbasesink_class->render = gst_vlc_video_sink_chain;
 }
 
@@ -262,6 +267,52 @@ invalid_caps:
     }
 }
 
+gboolean gst_vlc_video_sink_query_caps( GstQuery *p_query )
+{
+    GstCaps *p_query_caps;
+    gst_query_parse_caps( p_query, &p_query_caps );
+
+    if( p_query_caps == NULL )
+        return FALSE;
+
+    /* gst_caps_normalize takes ownership of the cap  */
+    gst_caps_ref( p_query_caps );
+    GstCaps *p_normalized = gst_caps_normalize( p_query_caps );
+    p_normalized = gst_caps_make_writable( p_normalized );
+
+    guint i = 0;
+    while( i < gst_caps_get_size( p_normalized ) )
+    {
+        GstStructure *p_str = gst_caps_get_structure( p_normalized, i );
+        const char* psz_fourcc = gst_structure_get_string( p_str, "format" );
+
+        if( ( psz_fourcc != NULL ) && gst_vlc_to_map_format( psz_fourcc ) ==
+                                          VLC_CODEC_UNKNOWN )
+        {
+            gst_caps_remove_structure( p_normalized, i );
+        }
+        else
+        {
+            i++;
+        }
+    }
+
+    gst_query_set_caps_result( p_query, p_normalized );
+
+    return TRUE;
+}
+
+static gboolean gst_vlc_video_sink_query( GstBaseSink *p_bsink,
+        GstQuery *p_query )
+{
+    if( GST_QUERY_TYPE( p_query ) == GST_QUERY_CAPS )
+    {
+        return gst_vlc_video_sink_query_caps( p_query );
+    }
+
+    return GST_BASE_SINK_CLASS( parent_class )->query( p_bsink, p_query );
+}
+
 static GstFlowReturn gst_vlc_video_sink_chain( GstBaseSink *p_bsink,
         GstBuffer *p_buffer )
 {


=====================================
modules/codec/gstreamer/gstvlcvideosink.h
=====================================
@@ -81,4 +81,6 @@ struct _GstVlcVideoSinkClass
 
 GType gst_vlc_video_sink_get_type (void);
 
+gboolean gst_vlc_video_sink_query_caps( GstQuery *p_query );
+
 #endif /* __GST_VLC_VIDEO_SINK_H__ */


=====================================
modules/video_chroma/Makefile.am
=====================================
@@ -89,6 +89,15 @@ if HAVE_DARWIN
 chroma_LTLIBRARIES += libcvpx_plugin.la
 endif
 
+libgst_mem_plugin_la_SOURCES = video_chroma/gst_mem.c \
+	codec/gstreamer/gstcopypicture.c codec/gstreamer/gstcopypicture.h codec/gstreamer/gst_mem.h
+libgst_mem_plugin_la_CFLAGS = $(AM_CFLAGS) $(GST_VIDEO_CFLAGS) $(GST_APP_CFLAGS)
+libgst_mem_plugin_la_LIBADD = $(GST_VIDEO_LIBS) $(GST_APP_LIBS)
+
+if HAVE_GST_DECODE
+chroma_LTLIBRARIES += libgst_mem_plugin.la
+endif
+
 # Tests
 chroma_copy_sse_test_SOURCES = $(libchroma_copy_la_SOURCES)
 chroma_copy_sse_test_CFLAGS = -DCOPY_TEST


=====================================
modules/video_chroma/gst_mem.c
=====================================
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ * gst_mem.c: GStreamer Memory to picture converter
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Author: Yann Lochet <yann at l0chet.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/video-format.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_filter.h>
+#include <vlc_picture.h>
+
+#include "../codec/gstreamer/gstcopypicture.h"
+#include "../codec/gstreamer/gst_mem.h"
+
+static picture_t * Filter(filter_t *p_filter, picture_t *src)
+{
+    struct gst_mem_pic_context *pctx = container_of(src->context,
+                                                    struct gst_mem_pic_context, s);
+    GstBuffer *p_buf = pctx->p_buf;
+
+    picture_t *dst = filter_NewPicture(p_filter);
+    if (!dst)
+        return NULL;
+    picture_CopyProperties(dst, src);
+
+    GstVideoFrame frame;
+    if (unlikely(!gst_video_frame_map(&frame, pctx->p_vinfo, p_buf, GST_MAP_READ)))
+    {
+        msg_Err(p_filter, "failed to map gst video frame");
+        return NULL;
+    }
+
+    gst_CopyPicture(dst, &frame);
+    gst_video_frame_unmap(&frame);
+
+    picture_Release(src);
+    return dst;
+}
+
+static picture_t * Filter_chain( filter_t *p_filter, picture_t *src )
+{
+    filter_chain_t *p_chain = p_filter->p_sys;
+    return filter_chain_VideoFilter(p_chain, src);
+}
+
+static const struct vlc_filter_operations filter_ops = {
+    .filter_video = Filter,
+};
+
+static const struct vlc_filter_operations chain_ops = {
+    .filter_video = Filter_chain,
+};
+
+static int Open(filter_t *p_filter)
+{
+    if(p_filter->fmt_in.video.i_chroma != VLC_CODEC_GST_MEM_OPAQUE)
+        return VLC_EGENERIC;
+
+    if(p_filter->fmt_out.video.i_chroma == VLC_CODEC_NV12)
+    {
+        p_filter->ops = &filter_ops;
+        return VLC_SUCCESS;
+    }
+
+    es_format_t fmt_intermediate;
+    es_format_Copy(&fmt_intermediate, &p_filter->fmt_out);
+    fmt_intermediate.video.i_chroma = fmt_intermediate.i_codec = VLC_CODEC_NV12;
+
+    filter_chain_t *p_chain = filter_chain_NewVideo(p_filter, false, &p_filter->owner);
+    if (p_chain == NULL)
+        return VLC_ENOMEM;
+    filter_chain_Reset(p_chain, &p_filter->fmt_in, p_filter->vctx_in, &p_filter->fmt_out);
+
+    int ret;
+    ret = filter_chain_AppendConverter(p_chain, &fmt_intermediate);
+    if (ret != 0)
+        return VLC_EGENERIC;
+
+    ret = filter_chain_AppendConverter(p_chain, NULL);
+    if (ret != 0)
+        return VLC_EGENERIC;
+
+    p_filter->p_sys = p_chain;
+    p_filter->ops = &chain_ops;
+
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+    set_shortname(N_("GST_MEM converter"))
+    set_description(N_("GST_MEM Chroma Converter filter"))
+    set_subcategory(SUBCAT_VIDEO_VFILTER)
+    set_callback_video_converter(Open, 10)
+vlc_module_end()


=====================================
modules/video_output/Makefile.am
=====================================
@@ -119,6 +119,12 @@ vout_LTLIBRARIES += \
 	libcaeagl_ios_plugin.la
 endif
 
+libglinterop_gst_mem_plugin_la_SOURCES = video_output/opengl/interop_gst_mem.c \
+	video_output/opengl/interop.h codec/gstreamer/gst_mem.h
+libglinterop_gst_mem_plugin_la_CFLAGS = $(AM_CFLAGS) $(GL_CFLAGS) $(GST_VIDEO_CFLAGS) \
+	$(GST_APP_CFLAGS)
+libglinterop_gst_mem_plugin_la_LIBADD = $(GST_VIDEO_LIBS) $(GST_APP_LIBS)
+
 libglinterop_vaapi_plugin_la_SOURCES = video_output/opengl/interop_vaapi.c \
 	video_output/opengl/interop.h \
 	hw/vaapi/vlc_vaapi.c hw/vaapi/vlc_vaapi.h
@@ -135,6 +141,10 @@ if HAVE_EGL
 if HAVE_VAAPI
 vout_LTLIBRARIES += libglinterop_vaapi_plugin.la
 endif
+
+if HAVE_GST_DECODE
+vout_LTLIBRARIES += libglinterop_gst_mem_plugin.la
+endif
 endif # HAVE_EGL
 
 if HAVE_VDPAU


=====================================
modules/video_output/opengl/interop_gst_mem.c
=====================================
@@ -0,0 +1,276 @@
+/*****************************************************************************
+ * interop_gst_mem.c: OpenGL GStreamer Memory opaque converter
+ *****************************************************************************
+ * Copyright (C) 2022 VLC authors and VideoLAN
+ *
+ * Author: Yann Lochet <yann at l0chet.fr>
+ * Heavily inspired by interop_vaapi.c
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/video-format.h>
+#include <gst/allocators/gstdmabuf.h>
+
+#include <vlc_common.h>
+#include <vlc_window.h>
+#include <vlc_codec.h>
+#include <vlc_plugin.h>
+
+#include "gl_util.h"
+#include "interop.h"
+
+#include "../../codec/gstreamer/gst_mem.h"
+
+#define DRM_FORMAT_MOD_LINEAR 0ULL
+
+struct priv
+{
+    PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
+
+    struct
+    {
+        EGLDisplay display;
+        EGLDisplay (*getCurrentDisplay)();
+        const char *(*queryString)(EGLDisplay, EGLint);
+        EGLImage (*createImageKHR)(EGLDisplay, EGLContext, EGLenum target, EGLClientBuffer buffer,
+                const EGLint *attrib_list);
+        void (*destroyImageKHR)(EGLDisplay, EGLImage image);
+    } egl;
+
+    struct
+    {
+        PFNGLBINDTEXTUREPROC BindTexture;
+    } gl;
+
+    EGLint drm_fourccs[3];
+};
+
+static void
+egl_image_destroy(const struct vlc_gl_interop *interop, EGLImageKHR image)
+{
+    struct priv *priv = interop->priv;
+    priv->egl.destroyImageKHR(priv->egl.display, image);
+}
+
+static int
+egl_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+           const int32_t tex_width[], const int32_t tex_height[],
+           picture_t *pic, const size_t *plane_offset)
+{
+    (void) plane_offset;
+    struct priv *priv = interop->priv;
+
+    struct gst_mem_pic_context *pctx = container_of( pic->context,
+                                                     struct gst_mem_pic_context, s );
+    GstBuffer *p_buf = pctx->p_buf;
+    GstVideoMeta *p_meta = gst_buffer_get_video_meta(p_buf);
+    GstMemory *p_mem = gst_buffer_peek_memory(p_buf, 0);
+    int dmabuf_fd = gst_dmabuf_memory_get_fd(p_mem);
+
+    int egl_color_space;
+    switch (interop->fmt_in.space)
+    {
+        case COLOR_SPACE_BT601:
+            egl_color_space = EGL_ITU_REC601_EXT;
+            break;
+        case COLOR_SPACE_BT709:
+            egl_color_space = EGL_ITU_REC709_EXT;
+            break;
+        case COLOR_SPACE_BT2020:
+            egl_color_space = EGL_ITU_REC2020_EXT;
+            break;
+        default:
+            goto error;
+    }
+
+    int egl_color_range;
+    switch (interop->fmt_in.color_range)
+    {
+        case COLOR_RANGE_FULL:
+            egl_color_range = EGL_YUV_FULL_RANGE_EXT;
+            break;
+        case COLOR_RANGE_LIMITED:
+            egl_color_range = EGL_YUV_NARROW_RANGE_EXT;
+            break;
+        default:
+            vlc_assert_unreachable();
+    }
+
+    const EGLint attribs[] = {
+        EGL_WIDTH, tex_width[0],
+        EGL_HEIGHT, tex_height[0],
+        EGL_LINUX_DRM_FOURCC_EXT, priv->drm_fourccs[0],
+        EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf_fd,
+        EGL_DMA_BUF_PLANE0_OFFSET_EXT, p_meta->offset[0],
+        EGL_DMA_BUF_PLANE0_PITCH_EXT, p_meta->stride[0],
+        EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, DRM_FORMAT_MOD_LINEAR & 0xffffffff,
+        EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff,
+        EGL_DMA_BUF_PLANE1_FD_EXT, dmabuf_fd,
+        EGL_DMA_BUF_PLANE1_OFFSET_EXT, p_meta->offset[1],
+        EGL_DMA_BUF_PLANE1_PITCH_EXT, p_meta->stride[1],
+        EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, DRM_FORMAT_MOD_LINEAR & 0xffffffff,
+        EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, (DRM_FORMAT_MOD_LINEAR >> 32) & 0xffffffff,
+        EGL_YUV_COLOR_SPACE_HINT_EXT, egl_color_space,
+        EGL_SAMPLE_RANGE_HINT_EXT, egl_color_range,
+        EGL_NONE
+    };
+
+    EGLImageKHR egl_image = priv->egl.createImageKHR(priv->egl.display, EGL_NO_CONTEXT,
+                                                     EGL_LINUX_DMA_BUF_EXT, NULL,
+                                                     attribs);
+
+    if( egl_image == NULL )
+        goto error;
+
+    priv->gl.BindTexture(interop->tex_target, textures[0]);
+
+    priv->glEGLImageTargetTexture2DOES(interop->tex_target, egl_image);
+
+    egl_image_destroy(interop, egl_image);
+
+    return VLC_SUCCESS;
+
+error:
+    return VLC_EGENERIC;
+}
+
+static void
+Close(struct vlc_gl_interop *interop)
+{
+    struct priv *priv = interop->priv;
+
+    free(priv);
+}
+
+static int
+Open(vlc_object_t *obj)
+{
+    struct vlc_gl_interop *interop = (void *) obj;
+    struct priv *priv = NULL;
+
+    if (interop->vctx == NULL)
+        return VLC_EGENERIC;
+    vlc_decoder_device *dec_device = vlc_video_context_HoldDevice(interop->vctx);
+    if (dec_device->type != VLC_DECODER_DEVICE_GSTDECODE)
+        goto error;
+
+    priv = interop->priv = calloc(1, sizeof(struct priv));
+    if (unlikely(priv == NULL))
+        goto error;
+
+    switch (interop->fmt_in.i_chroma)
+    {
+        case VLC_CODEC_GST_MEM_OPAQUE:
+            interop->tex_count = 1;
+            interop->texs[0] = (struct vlc_gl_tex_cfg) {
+                .w = {1, 1},
+                .h = {1, 1},
+                .internal = GL_RGBA,
+                .format = GL_RGBA,
+                .type = GL_UNSIGNED_BYTE,
+            };
+
+            priv->drm_fourccs[0] = VLC_FOURCC('N', 'V', '1', '2');
+            break;
+        default:
+            goto error;
+    }
+
+    struct vlc_gl_extension_vt extension_vt;
+    vlc_gl_LoadExtensionFunctions(interop->gl, &extension_vt);
+
+    /* GL_OES_EGL_image_external is required for GL_TEXTURE_EXTERNAL_OES */
+    if (!vlc_gl_HasExtension(&extension_vt, "GL_OES_EGL_image_external"))
+        goto error;
+
+    priv->egl.getCurrentDisplay = vlc_gl_GetProcAddress(interop->gl, "eglGetCurrentDisplay");
+    if (priv->egl.getCurrentDisplay == EGL_NO_DISPLAY)
+        goto error;
+
+    priv->egl.display = priv->egl.getCurrentDisplay();
+    if (priv->egl.display == EGL_NO_DISPLAY)
+        goto error;
+
+    priv->egl.queryString = vlc_gl_GetProcAddress(interop->gl, "eglQueryString");
+    if (priv->egl.queryString == NULL)
+        goto error;
+
+    /* EGL_EXT_image_dma_buf_import implies EGL_KHR_image_base */
+    const char *eglexts = priv->egl.queryString(priv->egl.display, EGL_EXTENSIONS);
+    if (eglexts == NULL || !vlc_gl_StrHasToken(eglexts, "EGL_EXT_image_dma_buf_import"))
+        goto error;
+
+    priv->egl.createImageKHR =
+        vlc_gl_GetProcAddress(interop->gl, "eglCreateImageKHR");
+    if (priv->egl.createImageKHR == NULL)
+        goto error;
+
+    priv->egl.destroyImageKHR =
+        vlc_gl_GetProcAddress(interop->gl, "eglDestroyImageKHR");
+    if (priv->egl.destroyImageKHR == NULL)
+        goto error;
+
+    priv->glEGLImageTargetTexture2DOES =
+        vlc_gl_GetProcAddress(interop->gl, "glEGLImageTargetTexture2DOES");
+    if (priv->glEGLImageTargetTexture2DOES == NULL)
+        goto error;
+
+    priv->gl.BindTexture =
+        vlc_gl_GetProcAddress(interop->gl, "glBindTexture");
+    if (priv->gl.BindTexture == NULL)
+        goto error;
+
+    /* The pictures are uploaded upside-down */
+    video_format_TransformBy(&interop->fmt_out, TRANSFORM_VFLIP);
+
+    interop->tex_target = GL_TEXTURE_EXTERNAL_OES;
+    interop->fmt_out.i_chroma = VLC_CODEC_RGB32;
+    interop->fmt_out.space = COLOR_SPACE_UNDEF;
+
+    static const struct vlc_gl_interop_ops ops = {
+        .update_textures = egl_update,
+        .close = Close,
+    };
+    interop->ops = &ops;
+
+    vlc_decoder_device_Release(dec_device);
+
+    return VLC_SUCCESS;
+
+error:
+    vlc_decoder_device_Release(dec_device);
+    free(priv);
+    return VLC_EGENERIC;
+}
+
+vlc_module_begin ()
+    set_description("GST_MEM OpenGL surface converter")
+    set_capability("glinterop", 1)
+    set_callback(Open)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    add_shortcut("gst_mem")
+vlc_module_end ()


=====================================
src/misc/fourcc.c
=====================================
@@ -835,6 +835,8 @@ static const struct
 
     { { VLC_CODEC_CVPX_P010, 0 },              FAKE_FMT() },
 
+    { { VLC_CODEC_GST_MEM_OPAQUE, 0 },         FAKE_FMT() },
+
     { { VLC_CODEC_VAAPI_420, VLC_CODEC_VAAPI_420_10BPP },
                                                FAKE_FMT() },
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1d312f1dd7d5199a934d715a919f704edd44aa20...09f99896cd1eef0b93123e5d8249dc044fbec233

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1d312f1dd7d5199a934d715a919f704edd44aa20...09f99896cd1eef0b93123e5d8249dc044fbec233
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