]> code.ossystems Code Review - meta-freescale.git/commitdiff
gstreamer: Add new uploader and GBM GL backend for video playback via GL 28/15828/1
authorCarlos Rafael Giani <dv7777@gmail.com>
Mon, 12 Jun 2017 22:27:20 +0000 (00:27 +0200)
committerCarlos Rafael Giani <dv7777@gmail.com>
Mon, 12 Jun 2017 22:27:20 +0000 (00:27 +0200)
Change-Id: I711bf82193279070d922ce6e2427268ce8989aac
Signed-off-by: Carlos Rafael Giani <dv7777@gmail.com>
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0001-gstgl-Fix-sanity-check-in-gst_gl_memory_setup_buffer.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0002-gl-egl-Add-gst_egl_image_from_dmabuf_direct-function.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0003-gstgl-Add-direct-DMABUF-uploader-WIP.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0001-qmlglsink-Add-dummy-texture-that-is-shown-as-placeho.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0002-gl-viv-fb-Fix-user-choice-string-comparisons.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0003-configure-Factor-out-libdrm-to-make-the-configuratio.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0004-configure-Factor-out-gudev-checks.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0005-gl-Add-Mesa3D-GBM-backend.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.12.%.bbappend [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-good/0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch [new file with mode: 0644]
recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.12.%.bbappend [new file with mode: 0644]

diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0001-gstgl-Fix-sanity-check-in-gst_gl_memory_setup_buffer.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0001-gstgl-Fix-sanity-check-in-gst_gl_memory_setup_buffer.patch
new file mode 100644 (file)
index 0000000..fce6d3b
--- /dev/null
@@ -0,0 +1,28 @@
+From f8ff1002df589853edce4aee2a2d3714a6f2971f Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 7 Jun 2017 18:00:08 +0200
+Subject: [PATCH 1/3] gstgl: Fix sanity check in gst_gl_memory_setup_buffer()
+
+---
+ gst-libs/gst/gl/gstglmemory.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/gst-libs/gst/gl/gstglmemory.c b/gst-libs/gst/gl/gstglmemory.c
+index 02848f0..7060f1a 100644
+--- a/gst-libs/gst/gl/gstglmemory.c
++++ b/gst-libs/gst/gl/gstglmemory.c
+@@ -1454,8 +1454,10 @@ gst_gl_memory_setup_buffer (GstGLMemoryAllocator * allocator,
+   else
+     views = 1;
++  /* Sanity check for the code below; either, there are no wrapped pointers,
++   * or there are at least as many pointers as there are memory blocks */
+   g_return_val_if_fail (!wrapped_data
+-      || views * n_mem != n_wrapped_pointers, FALSE);
++      || n_mem >= n_wrapped_pointers, FALSE);
+   for (v = 0; v < views; v++) {
+     for (i = 0; i < n_mem; i++) {
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0002-gl-egl-Add-gst_egl_image_from_dmabuf_direct-function.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0002-gl-egl-Add-gst_egl_image_from_dmabuf_direct-function.patch
new file mode 100644 (file)
index 0000000..14c5b56
--- /dev/null
@@ -0,0 +1,341 @@
+From 3232363bfb6a062023514416883a7ed50802a48b Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 7 Jun 2017 18:10:38 +0200
+Subject: [PATCH 2/3] gl/egl: Add gst_egl_image_from_dmabuf_direct() function
+
+This also renames the existing gst_egl_image_from_dmabuf() to clarify the
+difference between the two
+---
+ docs/libs/gst-plugins-bad-libs-sections.txt |   3 +-
+ gst-libs/gst/gl/egl/gsteglimage.c           | 239 +++++++++++++++++++++++++++-
+ gst-libs/gst/gl/egl/gsteglimage.h           |   6 +-
+ 3 files changed, 238 insertions(+), 10 deletions(-)
+
+diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt
+index 3dc5877..595f812 100644
+--- a/docs/libs/gst-plugins-bad-libs-sections.txt
++++ b/docs/libs/gst-plugins-bad-libs-sections.txt
+@@ -1166,7 +1166,8 @@ GstGLBaseFilterPrivate
+ <SECTION>
+ <FILE>gsteglimage</FILE>
+ <TITLE>GstEGLImage</TITLE>
+-gst_egl_image_from_dmabuf
++gst_egl_image_from_dmabuf_as_rgba_plane
++gst_egl_image_from_dmabuf_direct
+ gst_egl_image_from_texture
+ gst_egl_image_get_image
+ gst_egl_image_new_wrapped
+diff --git a/gst-libs/gst/gl/egl/gsteglimage.c b/gst-libs/gst/gl/egl/gsteglimage.c
+index f570718..ad8b8e8 100644
+--- a/gst-libs/gst/gl/egl/gsteglimage.c
++++ b/gst-libs/gst/gl/egl/gsteglimage.c
+@@ -29,9 +29,9 @@
+  *
+  * #GstEGLImage represents and holds an #EGLImage handle.
+  *
+- * A #GstEGLImage can be created from a dmabuf with gst_egl_image_from_dmabuf()
+- * or #GstGLMemoryEGL provides a #GstAllocator to allocate #EGLImage's bound to
+- * and OpenGL texture.
++ * A #GstEGLImage can be created from a dmabuf with gst_egl_image_from_dmabuf_direct()
++ * or gst_egl_image_from_dmabuf_as_rgba_plane(), or #GstGLMemoryEGL provides a
++ * #GstAllocator to allocate #EGLImage's bound to and OpenGL texture.
+  */
+ #ifdef HAVE_CONFIG_H
+@@ -337,12 +337,12 @@ gst_egl_image_from_texture (GstGLContext * context, GstGLMemory * gl_mem,
+ /*
+  * GStreamer format descriptions differ from DRM formats as the representation
+  * is relative to a register, hence in native endianness. To reduce the driver
+- * requirement, we only import with a subset of texture formats and use
++ * requirement, we only import with a subset of RGBA texture formats and use
+  * shaders to convert. This way we avoid having to use external texture
+  * target.
+  */
+ static int
+-_drm_fourcc_from_info (GstVideoInfo * info, int plane)
++_drm_rgba_fourcc_from_info (GstVideoInfo * info, int plane)
+ {
+   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
+ #if G_BYTE_ORDER == G_LITTLE_ENDIAN
+@@ -404,18 +404,112 @@ _drm_fourcc_from_info (GstVideoInfo * info, int plane)
+   }
+ }
++/*
++ * Variant of _drm_rgba_fourcc_from_info() that is used in case the GPU can
++ * handle YUV formats directly (by using internal shaders, or hardwired
++ * YUV->RGB conversion matrices etc.)
++ */
++static int
++_drm_direct_fourcc_from_info (GstVideoInfo * info)
++{
++  GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
++
++  GST_DEBUG ("Getting DRM fourcc for %s", gst_video_format_to_string (format));
++
++  switch (format) {
++    case GST_VIDEO_FORMAT_YUY2:
++      return DRM_FORMAT_YUYV;
++
++    case GST_VIDEO_FORMAT_YVYU:
++      return DRM_FORMAT_YVYU;
++
++    case GST_VIDEO_FORMAT_UYVY:
++      return DRM_FORMAT_UYVY;
++
++    case GST_VIDEO_FORMAT_VYUY:
++      return DRM_FORMAT_VYUY;
++
++    case GST_VIDEO_FORMAT_AYUV:
++      return DRM_FORMAT_AYUV;
++
++    case GST_VIDEO_FORMAT_NV12:
++      return DRM_FORMAT_NV12;
++
++    case GST_VIDEO_FORMAT_NV21:
++      return DRM_FORMAT_NV21;
++
++    case GST_VIDEO_FORMAT_NV16:
++      return DRM_FORMAT_NV16;
++
++    case GST_VIDEO_FORMAT_NV61:
++      return DRM_FORMAT_NV61;
++
++    case GST_VIDEO_FORMAT_NV24:
++      return DRM_FORMAT_NV24;
++
++    case GST_VIDEO_FORMAT_YUV9:
++      return DRM_FORMAT_YUV410;
++
++    case GST_VIDEO_FORMAT_YVU9:
++      return DRM_FORMAT_YVU410;
++
++    case GST_VIDEO_FORMAT_Y41B:
++      return DRM_FORMAT_YUV411;
++
++    case GST_VIDEO_FORMAT_I420:
++      return DRM_FORMAT_YUV420;
++
++    case GST_VIDEO_FORMAT_YV12:
++      return DRM_FORMAT_YVU420;
++
++    case GST_VIDEO_FORMAT_Y42B:
++      return DRM_FORMAT_YUV422;
++
++    case GST_VIDEO_FORMAT_Y444:
++      return DRM_FORMAT_YUV444;
++
++    case GST_VIDEO_FORMAT_RGB16:
++    case GST_VIDEO_FORMAT_BGR16:
++    case GST_VIDEO_FORMAT_RGB:
++    case GST_VIDEO_FORMAT_BGR:
++    case GST_VIDEO_FORMAT_RGBA:
++    case GST_VIDEO_FORMAT_RGBx:
++    case GST_VIDEO_FORMAT_BGRA:
++    case GST_VIDEO_FORMAT_BGRx:
++    case GST_VIDEO_FORMAT_ARGB:
++    case GST_VIDEO_FORMAT_xRGB:
++    case GST_VIDEO_FORMAT_ABGR:
++    case GST_VIDEO_FORMAT_xBGR:
++    case GST_VIDEO_FORMAT_GRAY8:
++      /* Use _drm_rgba_fourcc_from_info() as fallback for RGB formats */
++      return _drm_rgba_fourcc_from_info (info, 0);
++
++    default:
++      GST_ERROR ("Unsupported format for DMABuf.");
++      return -1;
++  }
++}
++
+ /**
+- * gst_egl_image_from_dmabuf:
++ * gst_egl_image_from_dmabuf_as_rgba_plane:
+  * @context: a #GstGLContext (must be an EGL context)
+  * @dmabuf: the DMA-Buf file descriptor
+  * @in_info: the #GstVideoInfo in @dmabuf
+  * @plane: the plane in @in_info to create and #GstEGLImage for
+  * @offset: the byte-offset in the data
+  *
++ * Creates an EGL image that imports the dmabuf FD. The dmabuf data
++ * is passed as RGBA data. Shaders later take this "RGBA" data and
++ * convert it from its true format (described by in_info) to actual
++ * RGBA output. For example, with I420, three EGL images are created,
++ * one for each plane, each EGL image with a single-channel R format.
++ * With NV12, two EGL images are created, one with R format, one
++ * with RG format etc.
++ *
+  * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
+  */
+ GstEGLImage *
+-gst_egl_image_from_dmabuf (GstGLContext * context,
++gst_egl_image_from_dmabuf_as_rgba_plane (GstGLContext * context,
+     gint dmabuf, GstVideoInfo * in_info, gint plane, gsize offset)
+ {
+   GstGLFormat format;
+@@ -425,7 +519,7 @@ gst_egl_image_from_dmabuf (GstGLContext * context,
+   gint fourcc;
+   gint i;
+-  fourcc = _drm_fourcc_from_info (in_info, plane);
++  fourcc = _drm_rgba_fourcc_from_info (in_info, plane);
+   format = gst_gl_format_from_video_info (context, in_info, plane);
+   GST_DEBUG ("fourcc %.4s (%d) plane %d (%dx%d)",
+@@ -462,4 +556,133 @@ gst_egl_image_from_dmabuf (GstGLContext * context,
+   return gst_egl_image_new_wrapped (context, img, format, NULL,
+       (GstEGLImageDestroyNotify) _destroy_egl_image);
+ }
++
++/**
++ * gst_egl_image_from_dmabuf_direct:
++ * @context: a #GstGLContext (must be an EGL context)
++ * @buffer: the #GstBuffer containing DMA-Buf memory
++ * @in_info: the #GstVideoInfo in @dmabuf
++ *
++ * Creates an EGL image that imports the dmabuf FD. The dmabuf data
++ * is passed directly as the format described in in_info. This is
++ * useful if the hardware is capable of performing color space conversions
++ * internally. The appropriate DRM format is picked, and the EGL image
++ * is created with this DRM format.
++ *
++ * Another notable difference to gst_egl_image_from_dmabuf_as_rgba_plane()
++ * is that this function creates one EGL image for all planes, not just one.
++ *
++ * Returns: a #GstEGLImage wrapping @dmabuf or %NULL on failure
++ */
++GstEGLImage *
++gst_egl_image_from_dmabuf_direct (GstGLContext * context,
++    GstBuffer * buffer, GstVideoInfo * in_info)
++{
++#define MAX_NUM_DMA_BUF_PLANES (3)
++
++  guint mems_idx[MAX_NUM_DMA_BUF_PLANES];
++  gsize mems_skip[MAX_NUM_DMA_BUF_PLANES];
++  GstMemory *mems[MAX_NUM_DMA_BUF_PLANES];
++  EGLImageKHR img;
++  guint n_planes = GST_VIDEO_INFO_N_PLANES (in_info);
++  gint fourcc = _drm_direct_fourcc_from_info (in_info);
++  gint i;
++
++  /* Explanation of array length:
++   * - 6 plane independent values are at the start (width, height, format FourCC)
++   * - 6 values per plane, and there are up to MAX_NUM_DMA_BUF_PLANES planes
++   * - 1 extra value for the EGL_NONE sentinel
++   */
++  guintptr attribs[6 + 6 * (MAX_NUM_DMA_BUF_PLANES) + 1] = {
++    EGL_WIDTH, GST_VIDEO_INFO_WIDTH (in_info),
++    EGL_HEIGHT, GST_VIDEO_INFO_HEIGHT (in_info),
++    EGL_LINUX_DRM_FOURCC_EXT, fourcc
++  };
++
++  static const EGLint egl_dmabuf_plane_fd_attr[MAX_NUM_DMA_BUF_PLANES] = {
++    EGL_DMA_BUF_PLANE0_FD_EXT,
++    EGL_DMA_BUF_PLANE1_FD_EXT,
++    EGL_DMA_BUF_PLANE2_FD_EXT,
++  };
++  static const EGLint egl_dmabuf_plane_offset_attr[MAX_NUM_DMA_BUF_PLANES] = {
++    EGL_DMA_BUF_PLANE0_OFFSET_EXT,
++    EGL_DMA_BUF_PLANE1_OFFSET_EXT,
++    EGL_DMA_BUF_PLANE2_OFFSET_EXT,
++  };
++  static const EGLint egl_dmabuf_plane_pitch_attr[MAX_NUM_DMA_BUF_PLANES] = {
++    EGL_DMA_BUF_PLANE0_PITCH_EXT,
++    EGL_DMA_BUF_PLANE1_PITCH_EXT,
++    EGL_DMA_BUF_PLANE2_PITCH_EXT,
++  };
++
++  /* Make sure we never set up more than MAX_NUM_DMA_BUF_PLANES planes */
++  if (G_UNLIKELY (n_planes > MAX_NUM_DMA_BUF_PLANES))
++    n_planes = MAX_NUM_DMA_BUF_PLANES;
++
++  GST_DEBUG ("Setting up EGL image attributes for %u plane(s)", n_planes);
++
++  for (i = 0; i < n_planes; i++) {
++    guint plane_size;
++    guint length;
++    gsize offset, stride;
++    gint fd;
++
++    plane_size = gst_gl_get_plane_data_size (in_info, NULL, i);
++
++    /* In the buffer, find the memory block that corresponds to the current plane,
++     * and note down the offset (which is needed for the EGLImage) and the index
++     * of the memory block */
++    if (!gst_buffer_find_memory (buffer, in_info->offset[i], plane_size,
++            &mems_idx[i], &length, &mems_skip[i])) {
++      GST_DEBUG ("Couldn't find memory for plane %d", i);
++      return NULL;
++    }
++
++    /* We can't have more then one dmabuf per plane */
++    if (length != 1) {
++      GST_DEBUG
++          ("There are multiple dmabufs for plane %d, which is unsupported", i);
++      return NULL;
++    }
++
++    /* Get a pointer to the memory block we found earlier */
++    mems[i] = gst_buffer_peek_memory (buffer, mems_idx[i]);
++
++    /* And all memory found must be dmabuf */
++    if (!gst_is_dmabuf_memory (mems[i])) {
++      GST_DEBUG ("Plane %d memory isn't dmabuf", i);
++      return NULL;
++    }
++
++    fd = gst_dmabuf_memory_get_fd (mems[i]);
++    offset = mems[i]->offset + mems_skip[i];
++    stride = GST_VIDEO_INFO_PLANE_STRIDE (in_info, i);
++
++    /* Set up configuration for the i-th plane */
++    attribs[6 + 6 * i + 0] = egl_dmabuf_plane_fd_attr[i];
++    attribs[6 + 6 * i + 1] = fd;
++    attribs[6 + 6 * i + 2] = egl_dmabuf_plane_offset_attr[i];
++    attribs[6 + 6 * i + 3] = offset;
++    attribs[6 + 6 * i + 4] = egl_dmabuf_plane_pitch_attr[i];
++    attribs[6 + 6 * i + 5] = stride;
++
++    GST_DEBUG ("Plane %d has FD %d offset %" G_GSIZE_FORMAT " stride %"
++        G_GSIZE_FORMAT, i, fd, offset, stride);
++  }
++
++  /* Add the EGL_NONE sentinel */
++  attribs[6 + 6 * n_planes] = EGL_NONE;
++
++  img = _gst_egl_image_create (context, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
++  if (!img) {
++    GST_WARNING ("eglCreateImage failed: %s",
++        gst_egl_get_error_string (eglGetError ()));
++    return NULL;
++  }
++
++  /* TODO: RGBA or RGB? */
++  return gst_egl_image_new_wrapped (context, img, GST_GL_RGB, NULL,
++      (GstEGLImageDestroyNotify) _destroy_egl_image);
++}
++
+ #endif /* GST_GL_HAVE_DMABUF */
+diff --git a/gst-libs/gst/gl/egl/gsteglimage.h b/gst-libs/gst/gl/egl/gsteglimage.h
+index 56ec7e1..613fc80 100644
+--- a/gst-libs/gst/gl/egl/gsteglimage.h
++++ b/gst-libs/gst/gl/egl/gsteglimage.h
+@@ -73,11 +73,15 @@ GstEGLImage *           gst_egl_image_from_texture              (GstGLContext *
+                                                                  GstGLMemory * gl_mem,
+                                                                  guintptr * attribs);
+ #if GST_GL_HAVE_DMABUF
+-GstEGLImage *           gst_egl_image_from_dmabuf               (GstGLContext * context,
++GstEGLImage *           gst_egl_image_from_dmabuf_as_rgba_plane (GstGLContext * context,
+                                                                  gint dmabuf,
+                                                                  GstVideoInfo * in_info,
+                                                                  gint plane,
+                                                                  gsize offset);
++
++GstEGLImage *           gst_egl_image_from_dmabuf_direct        (GstGLContext * context,
++                                                                 GstBuffer * buffer,
++                                                                 GstVideoInfo * in_info);
+ #endif
+ /**
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0003-gstgl-Add-direct-DMABUF-uploader-WIP.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0003-gstgl-Add-direct-DMABUF-uploader-WIP.patch
new file mode 100644 (file)
index 0000000..a2e7e54
--- /dev/null
@@ -0,0 +1,378 @@
+From 2d01625e1cf1100ed0c8b01ce8d04e56fd3198ef Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 7 Jun 2017 18:23:52 +0200
+Subject: [PATCH 3/3] gstgl: Add direct DMABUF uploader [WIP]
+
+This is the first, work-in-progress version of a new "direct" DMABUF
+uploader. The idea is that some GPUs (like the Vivante series) can actually
+perform the YUV->RGB conversion internally, so no custom conversion shaders
+are needed. To make use of this feature, we need an additional uploader
+that can import DMABUF FDs and also directly pass the pixel format,
+relying on the GPU to do the conversion.
+
+This patch is incomplete for these reasons:
+- It is currently unknown how to determine if the GPU can handle these
+  conversions
+- Currently it is not possible to probe for what EGLImage DMABUF input
+  formats the GPU can handle
+- The EGLImage cache is there, but cannot be used, because some drivers
+  like etnaviv keep a cached copy of the texture inside, and it is
+  currently not clear how to let etnaviv know that the cache has to be
+  flushed once the associated DMABUF gets a new video frame
+---
+ gst-libs/gst/gl/gstglupload.c | 290 +++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 256 insertions(+), 34 deletions(-)
+
+diff --git a/gst-libs/gst/gl/gstglupload.c b/gst-libs/gst/gl/gstglupload.c
+index 810f4fa..339739a 100644
+--- a/gst-libs/gst/gl/gstglupload.c
++++ b/gst-libs/gst/gl/gstglupload.c
+@@ -35,6 +35,7 @@
+ #if GST_GL_HAVE_DMABUF
+ #include <gst/allocators/gstdmabuf.h>
++#include <libdrm/drm_fourcc.h>
+ #endif
+ #if GST_GL_HAVE_VIV_DIRECTVIV
+@@ -477,7 +478,256 @@ static const UploadMethod _gl_memory_upload = {
+   &_gl_memory_upload_free
+ };
++
+ #if GST_GL_HAVE_DMABUF
++
++
++static GQuark
++_eglimage_quark (gint plane)
++{
++  static GQuark quark[4] = { 0 };
++  static const gchar *quark_str[] = {
++    "GstGLDMABufEGLImage0",
++    "GstGLDMABufEGLImage1",
++    "GstGLDMABufEGLImage2",
++    "GstGLDMABufEGLImage3",
++  };
++
++  if (!quark[plane])
++    quark[plane] = g_quark_from_static_string (quark_str[plane]);
++
++  return quark[plane];
++}
++
++static GstEGLImage *
++_get_cached_eglimage (GstMiniObject * mobj, gint plane)
++{
++  return gst_mini_object_get_qdata (mobj, _eglimage_quark (plane));
++}
++
++static void
++_set_cached_eglimage (GstMiniObject * mobj, GstEGLImage * eglimage, gint plane)
++{
++  return gst_mini_object_set_qdata (mobj,
++      _eglimage_quark (plane), eglimage, (GDestroyNotify) gst_egl_image_unref);
++}
++
++/* TODO: the normal and direct dmabuf uploader share a lot of code.
++ * Perhaps they can be merged. */
++struct DirectDmabufUpload
++{
++  GstGLUpload *upload;
++
++  GstEGLImage *eglimage;
++  GstBuffer *outbuf;
++  GstGLVideoAllocationParams *params;
++};
++
++/* TODO add format probing (requires EGL_EXT_image_dma_buf_import_modifiers)
++ * Currently this list is just hardcoded */
++#define GST_GL_DIRECT_DMABUF_FORMAT "{YUY2, NV12, I420}"
++
++static GstStaticCaps _direct_dma_buf_upload_caps =
++GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_GL_DIRECT_DMABUF_FORMAT));
++
++static gpointer
++_direct_dma_buf_upload_new (GstGLUpload * upload)
++{
++  struct DirectDmabufUpload *dmabuf = g_new0 (struct DirectDmabufUpload, 1);
++  dmabuf->upload = upload;
++  return dmabuf;
++}
++
++static GstCaps *
++_direct_dma_buf_upload_transform_caps (gpointer impl, GstGLContext * context,
++    GstPadDirection direction, GstCaps * caps)
++{
++  GstCapsFeatures *passthrough =
++      gst_caps_features_from_string
++      (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
++  GstCaps *ret;
++
++  if (direction == GST_PAD_SINK) {
++    GstCaps *tmp;
++
++    ret =
++        _set_caps_features_with_passthrough (caps,
++        GST_CAPS_FEATURE_MEMORY_GL_MEMORY, passthrough);
++
++    gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
++    tmp = _caps_intersect_texture_target (ret, 1 << GST_GL_TEXTURE_TARGET_2D);
++    gst_caps_unref (ret);
++    ret = tmp;
++  } else {
++    GstCaps *tmp;
++    tmp = gst_caps_from_string (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
++        (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, GST_GL_DIRECT_DMABUF_FORMAT));
++    ret =
++        _set_caps_features_with_passthrough (tmp,
++        GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, passthrough);
++    gst_caps_unref (tmp);
++  }
++
++  gst_caps_features_free (passthrough);
++  return ret;
++}
++
++static gboolean
++_direct_dma_buf_upload_accept (gpointer impl, GstBuffer * buffer,
++    GstCaps * in_caps, GstCaps * out_caps)
++{
++  struct DirectDmabufUpload *dmabuf = impl;
++  GstVideoInfo *in_info = &dmabuf->upload->priv->in_info;
++  guint n_planes = GST_VIDEO_INFO_N_PLANES (in_info);
++  GstVideoMeta *meta;
++  guint n_mem;
++  guint i;
++
++  n_mem = gst_buffer_n_memory (buffer);
++  meta = gst_buffer_get_video_meta (buffer);
++
++  /* dmabuf upload is only supported with EGL contexts. */
++  if (gst_gl_context_get_gl_platform (dmabuf->upload->context) !=
++      GST_GL_PLATFORM_EGL)
++    return FALSE;
++
++  if (!gst_gl_context_check_feature (dmabuf->upload->context,
++          "EGL_KHR_image_base"))
++    return FALSE;
++
++  /* This will eliminate most non-dmabuf out there */
++  if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (buffer, 0)))
++    return FALSE;
++
++  /* We cannot have multiple dmabuf per plane */
++  if (n_mem > n_planes)
++    return FALSE;
++
++  /* Update video info based on video meta */
++  if (meta) {
++    in_info->width = meta->width;
++    in_info->height = meta->height;
++
++    for (i = 0; i < meta->n_planes; i++) {
++      in_info->offset[i] = meta->offset[i];
++      in_info->stride[i] = meta->stride[i];
++    }
++  }
++
++  if (dmabuf->params)
++    gst_gl_allocation_params_free ((GstGLAllocationParams *) dmabuf->params);
++  if (!(dmabuf->params =
++          gst_gl_video_allocation_params_new_wrapped_gl_handle (dmabuf->
++              upload->context, NULL, &dmabuf->upload->priv->out_info, -1, NULL,
++              GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA, NULL,
++              NULL, NULL)))
++    return FALSE;
++
++  /* Now create an EGLImage for each dmabufs */
++  {
++    /* check if one is cached */
++    dmabuf->eglimage = _get_cached_eglimage (GST_MINI_OBJECT (buffer), 0);
++    /* TODO: This is a hack that effectively disables caching.
++     * Currently it is not possible to inform some GPU drivers (like etnaviv)
++     * that they should invalidate whatever texture-related cache they have,
++     * so even if the associated dmabuf changes, the texture doesn't.. So, as
++     * a workaround, were disable caching here and force the reinitialization
++     * of EGLImages. Fix the texture cache updates and then remove the #if
++     * block below. */
++#if 1
++    dmabuf->eglimage = NULL;
++#endif
++
++    if (!dmabuf->eglimage) {
++      /* otherwise create one and cache it */
++
++      dmabuf->eglimage =
++          gst_egl_image_from_dmabuf_direct (dmabuf->upload->context,
++          buffer, in_info);
++
++      if (!dmabuf->eglimage)
++        return FALSE;
++
++      _set_cached_eglimage (GST_MINI_OBJECT (buffer), dmabuf->eglimage, 0);
++    }
++
++    GST_TRACE ("Got GstEGLImage %p", (gpointer) (dmabuf->eglimage));
++  }
++
++  return TRUE;
++}
++
++static void
++_direct_dma_buf_upload_propose_allocation (gpointer impl,
++    GstQuery * decide_query, GstQuery * query)
++{
++  /* nothing to do for now. */
++}
++
++static void
++_direct_dma_buf_upload_perform_gl_thread (GstGLContext * context,
++    struct DirectDmabufUpload *dmabuf)
++{
++  GstGLMemoryAllocator *allocator;
++  gpointer ptr;
++
++  allocator =
++      GST_GL_MEMORY_ALLOCATOR (gst_allocator_find
++      (GST_GL_MEMORY_EGL_ALLOCATOR_NAME));
++
++  /* FIXME: buffer pool */
++  dmabuf->outbuf = gst_buffer_new ();
++  ptr = (gpointer *) dmabuf->eglimage;
++  gst_gl_memory_setup_buffer (allocator, dmabuf->outbuf, dmabuf->params, NULL,
++      &ptr, 1);
++  gst_object_unref (allocator);
++}
++
++static GstGLUploadReturn
++_direct_dma_buf_upload_perform (gpointer impl, GstBuffer * buffer,
++    GstBuffer ** outbuf)
++{
++  struct DirectDmabufUpload *dmabuf = impl;
++
++  gst_gl_context_thread_add (dmabuf->upload->context,
++      (GstGLContextThreadFunc) _direct_dma_buf_upload_perform_gl_thread,
++      dmabuf);
++
++  if (!dmabuf->outbuf)
++    return GST_GL_UPLOAD_ERROR;
++
++  gst_buffer_add_parent_buffer_meta (dmabuf->outbuf, buffer);
++
++  *outbuf = dmabuf->outbuf;
++  dmabuf->outbuf = NULL;
++
++  return GST_GL_UPLOAD_DONE;
++}
++
++static void
++_direct_dma_buf_upload_free (gpointer impl)
++{
++  struct DirectDmabufUpload *dmabuf = impl;
++
++  if (dmabuf->params)
++    gst_gl_allocation_params_free ((GstGLAllocationParams *) dmabuf->params);
++
++  g_free (impl);
++}
++
++static const UploadMethod _direct_dma_buf_upload = {
++  "DirectDmabuf",
++  0,
++  &_direct_dma_buf_upload_caps,
++  &_direct_dma_buf_upload_new,
++  &_direct_dma_buf_upload_transform_caps,
++  &_direct_dma_buf_upload_accept,
++  &_direct_dma_buf_upload_propose_allocation,
++  &_direct_dma_buf_upload_perform,
++  &_direct_dma_buf_upload_free
++};
++
++
+ struct DmabufUpload
+ {
+   GstGLUpload *upload;
+@@ -537,37 +787,6 @@ _dma_buf_upload_transform_caps (gpointer impl, GstGLContext * context,
+   return ret;
+ }
+-static GQuark
+-_eglimage_quark (gint plane)
+-{
+-  static GQuark quark[4] = { 0 };
+-  static const gchar *quark_str[] = {
+-    "GstGLDMABufEGLImage0",
+-    "GstGLDMABufEGLImage1",
+-    "GstGLDMABufEGLImage2",
+-    "GstGLDMABufEGLImage3",
+-  };
+-
+-  if (!quark[plane])
+-    quark[plane] = g_quark_from_static_string (quark_str[plane]);
+-
+-  return quark[plane];
+-}
+-
+-static GstEGLImage *
+-_get_cached_eglimage (GstMemory * mem, gint plane)
+-{
+-  return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
+-      _eglimage_quark (plane));
+-}
+-
+-static void
+-_set_cached_eglimage (GstMemory * mem, GstEGLImage * eglimage, gint plane)
+-{
+-  return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
+-      _eglimage_quark (plane), eglimage, (GDestroyNotify) gst_egl_image_unref);
+-}
+-
+ static gboolean
+ _dma_buf_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
+     GstCaps * out_caps)
+@@ -646,20 +865,20 @@ _dma_buf_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
+   /* Now create an EGLImage for each dmabufs */
+   for (i = 0; i < n_planes; i++) {
+     /* check if one is cached */
+-    dmabuf->eglimage[i] = _get_cached_eglimage (mems[i], i);
++    dmabuf->eglimage[i] = _get_cached_eglimage (GST_MINI_OBJECT (mems[i]), i);
+     if (dmabuf->eglimage[i])
+       continue;
+     /* otherwise create one and cache it */
+     dmabuf->eglimage[i] =
+-        gst_egl_image_from_dmabuf (dmabuf->upload->context,
++        gst_egl_image_from_dmabuf_as_rgba_plane (dmabuf->upload->context,
+         gst_dmabuf_memory_get_fd (mems[i]), in_info, i,
+         mems[i]->offset + mems_skip[i]);
+     if (!dmabuf->eglimage[i])
+       return FALSE;
+-    _set_cached_eglimage (mems[i], dmabuf->eglimage[i], i);
++    _set_cached_eglimage (GST_MINI_OBJECT (mems[i]), dmabuf->eglimage[i], i);
+   }
+   return TRUE;
+@@ -731,8 +950,10 @@ static const UploadMethod _dma_buf_upload = {
+   &_dma_buf_upload_free
+ };
++
+ #endif /* GST_GL_HAVE_DMABUF */
++
+ struct GLUploadMeta
+ {
+   GstGLUpload *upload;
+@@ -1511,6 +1732,7 @@ static const UploadMethod _directviv_upload = {
+ static const UploadMethod *upload_methods[] = { &_gl_memory_upload,
+ #if GST_GL_HAVE_DMABUF
++  &_direct_dma_buf_upload,
+   &_dma_buf_upload,
+ #endif
+ #if GST_GL_HAVE_VIV_DIRECTVIV
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0001-qmlglsink-Add-dummy-texture-that-is-shown-as-placeho.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0001-qmlglsink-Add-dummy-texture-that-is-shown-as-placeho.patch
new file mode 100644 (file)
index 0000000..adc8dc5
--- /dev/null
@@ -0,0 +1,107 @@
+From bb3edd3cc00068ae8f824e52aa656744e95be99c Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Sun, 21 May 2017 15:26:12 +0200
+Subject: [PATCH 1/5] qmlglsink: Add dummy texture that is shown as placeholder
+ for NULL buffers
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782917
+---
+ ext/qt/gstqsgtexture.cc | 41 +++++++++++++++++++++++++++++++++++++++++
+ ext/qt/gstqsgtexture.h  |  1 +
+ 2 files changed, 42 insertions(+)
+
+diff --git a/ext/qt/gstqsgtexture.cc b/ext/qt/gstqsgtexture.cc
+index 85747f2..7391806 100644
+--- a/ext/qt/gstqsgtexture.cc
++++ b/ext/qt/gstqsgtexture.cc
+@@ -22,6 +22,7 @@
+ #include "config.h"
+ #endif
++#include <vector>
+ #include <stdio.h>
+ #include <gst/video/video.h>
+@@ -47,12 +48,17 @@ GstQSGTexture::GstQSGTexture ()
+   this->buffer_ = NULL;
+   this->qt_context_ = NULL;
+   this->sync_buffer_ = gst_buffer_new ();
++  this->dummy_tex_id_ = 0;
+ }
+ GstQSGTexture::~GstQSGTexture ()
+ {
+   gst_buffer_replace (&this->buffer_, NULL);
+   gst_buffer_replace (&this->sync_buffer_, NULL);
++  if (this->dummy_tex_id_ && QOpenGLContext::currentContext ()) {
++    QOpenGLContext::currentContext ()->functions ()->glDeleteTextures (1,
++        &this->dummy_tex_id_);
++  }
+ }
+ /* only called from the streaming thread with scene graph thread blocked */
+@@ -87,6 +93,7 @@ GstQSGTexture::bind ()
+   GstGLSyncMeta *sync_meta;
+   GstMemory *mem;
+   guint tex_id;
++  gboolean use_dummy_tex = TRUE;
+   if (!this->qt_context_)
+     return;
+@@ -132,7 +139,41 @@ GstQSGTexture::bind ()
+   gst_video_frame_unmap (&this->v_frame);
++  /* Texture was successfully bound, so we do not need
++   * to use the dummy texture */
++  use_dummy_tex = FALSE;
++
+ out:
++  if (G_UNLIKELY (use_dummy_tex)) {
++    QOpenGLContext *qglcontext = QOpenGLContext::currentContext ();
++    QOpenGLFunctions *funcs = qglcontext->functions ();
++
++    /* Create dummy texture if not already present.
++     * Use the Qt OpenGL functions instead of the GstGL ones,
++     * since we are using the Qt OpenGL context here, and we must
++     * be able to delete the texture in the destructor. */
++    if (this->dummy_tex_id_ == 0) {
++      /* Make this a black 64x64 pixel RGBA texture.
++       * This size and format is supported pretty much everywhere, so these
++       * are a safe pick. (64 pixel sidelength must be supported according
++       * to the GLES2 spec, table 6.18.)
++       * Set min/mag filters to GL_LINEAR to make sure no mipmapping is used. */
++      const int tex_sidelength = 64;
++      std::vector < guint8 > dummy_data (tex_sidelength * tex_sidelength * 4, 0);
++
++      funcs->glGenTextures (1, &this->dummy_tex_id_);
++      funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_);
++      funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
++      funcs->glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
++      funcs->glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tex_sidelength,
++          tex_sidelength, 0, GL_RGBA, GL_UNSIGNED_BYTE, &dummy_data[0]);
++    }
++
++    g_assert (this->dummy_tex_id_ != 0);
++
++    funcs->glBindTexture (GL_TEXTURE_2D, this->dummy_tex_id_);
++  }
++
+   gst_gl_context_activate (this->qt_context_, FALSE);
+ }
+diff --git a/ext/qt/gstqsgtexture.h b/ext/qt/gstqsgtexture.h
+index a5a6f9f..fdabe93 100644
+--- a/ext/qt/gstqsgtexture.h
++++ b/ext/qt/gstqsgtexture.h
+@@ -51,6 +51,7 @@ private:
+     GstBuffer * sync_buffer_;
+     GstGLContext * qt_context_;
+     GstMemory * mem_;
++    GLuint dummy_tex_id_;
+     GstVideoInfo v_info;
+     GstVideoFrame v_frame;
+ };
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0002-gl-viv-fb-Fix-user-choice-string-comparisons.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0002-gl-viv-fb-Fix-user-choice-string-comparisons.patch
new file mode 100644 (file)
index 0000000..4cbf8d9
--- /dev/null
@@ -0,0 +1,40 @@
+From a1d44ee3fbc517b6515f41e98871b01a7608623c Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Tue, 9 May 2017 23:59:04 +0200
+Subject: [PATCH 2/5] gl/viv-fb: Fix user-choice string comparisons
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782921
+---
+ gst-libs/gst/gl/gstgldisplay.c | 2 +-
+ gst-libs/gst/gl/gstglwindow.c  | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/gst-libs/gst/gl/gstgldisplay.c b/gst-libs/gst/gl/gstgldisplay.c
+index b456b1f..6408f0c 100644
+--- a/gst-libs/gst/gl/gstgldisplay.c
++++ b/gst-libs/gst/gl/gstgldisplay.c
+@@ -301,7 +301,7 @@ gst_gl_display_new (void)
+     display = GST_GL_DISPLAY (gst_gl_display_wayland_new (NULL));
+ #endif
+ #if GST_GL_HAVE_WINDOW_VIV_FB
+-  if (!display && (!user_choice || g_strstr_len (user_choice, 2, "viv-fb"))) {
++  if (!display && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb"))) {
+     const gchar *disp_idx_str = NULL;
+     gint disp_idx = 0;
+     disp_idx_str = g_getenv ("GST_GL_VIV_FB");
+diff --git a/gst-libs/gst/gl/gstglwindow.c b/gst-libs/gst/gl/gstglwindow.c
+index c1dae2d..d279b22 100644
+--- a/gst-libs/gst/gl/gstglwindow.c
++++ b/gst-libs/gst/gl/gstglwindow.c
+@@ -275,7 +275,7 @@ gst_gl_window_new (GstGLDisplay * display)
+     window = GST_GL_WINDOW (gst_gl_window_eagl_new (display));
+ #endif
+ #if GST_GL_HAVE_WINDOW_VIV_FB
+-  if (!window && (!user_choice || g_strstr_len (user_choice, 2, "viv-fb")))
++  if (!window && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb")))
+     window = GST_GL_WINDOW (gst_gl_window_viv_fb_egl_new (display));
+ #endif
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0003-configure-Factor-out-libdrm-to-make-the-configuratio.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0003-configure-Factor-out-libdrm-to-make-the-configuratio.patch
new file mode 100644 (file)
index 0000000..0a15dce
--- /dev/null
@@ -0,0 +1,124 @@
+From 9e42946256f4cdc09b2c43d5779dbbfba464f684 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Mon, 29 May 2017 22:04:14 +0200
+Subject: [PATCH 3/5] configure: Factor out libdrm to make the configuration
+ reusable
+
+KMS and Wayland both use libdrm. Also, future EGL GBM backends will also
+need libdrm. For this reason it makes sense to check for it once and
+reuse the result instead of doing redundant checks.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782922
+---
+ configure.ac            | 35 +++++++++++++++++++++++------------
+ ext/wayland/Makefile.am |  6 ++++--
+ sys/kms/Makefile.am     |  4 ++--
+ 3 files changed, 29 insertions(+), 16 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 18ec79f..3a5e023 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -565,6 +565,9 @@ if test "x$HAVE_WINSOCK2_H" = "xyes"; then
+   AC_SUBST(WINSOCK2_LIBS)
+ fi
++dnl check for libdrm
++PKG_CHECK_MODULES(DRM, libdrm >= 2.4.55, HAVE_DRM=yes, HAVE_DRM=no)
++
+ dnl *** opengl ***
+ AC_ARG_ENABLE([opengl],
+      [  --enable-opengl         Enable Desktop OpenGL support @<:@default=auto@:>@],
+@@ -2261,17 +2264,21 @@ dnl **** Wayland ****
+ translit(dnm, m, l) AM_CONDITIONAL(USE_WAYLAND, true)
+ AC_PATH_PROG([wayland_scanner], [wayland-scanner])
+ AG_GST_CHECK_FEATURE(WAYLAND, [wayland sink], wayland , [
+-  PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.4.0 libdrm >= 2.4.55 wayland-protocols >= 1.4, [
+-    if test "x$wayland_scanner" != "x"; then
+-      HAVE_WAYLAND="yes"
+-      AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, ${WAYLAND_PROTOCOLS_SYSROOT_DIR}`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)
+-    else
+-      AC_MSG_RESULT([wayland-scanner is required to build the wayland plugin])
+-      HAVE_WAYLAND="no"
+-    fi
+-    ],
+-    [ HAVE_WAYLAND="no"
+-  ])
++  if test "x$HAVE_DRM" = "xyes"; then
++    PKG_CHECK_MODULES(WAYLAND, wayland-client >= 1.4.0 wayland-protocols >= 1.4, [
++      if test "x$wayland_scanner" != "x"; then
++        HAVE_WAYLAND="yes"
++        AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, ${WAYLAND_PROTOCOLS_SYSROOT_DIR}`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)
++      else
++        AC_MSG_RESULT([wayland-scanner is required to build the wayland plugin])
++        HAVE_WAYLAND="no"
++      fi
++      ],
++      [ HAVE_WAYLAND="no"
++    ])
++  else
++    HAVE_WAYLAND="no"
++  fi
+ ])
+ dnl **** WebP ****
+@@ -2429,7 +2436,11 @@ dnl *** kms ***
+ translit(dnm, m, l) AM_CONDITIONAL(USE_KMS, true)
+ AG_GST_CHECK_FEATURE(KMS, [drm/kms libraries], kms, [
+   AG_GST_PKG_CHECK_MODULES(GST_ALLOCATORS, gstreamer-allocators-1.0)
+-  PKG_CHECK_MODULES([KMS_DRM], [libdrm >= 2.4.55], HAVE_KMS=yes, HAVE_KMS=no)
++  if test "x$HAVE_DRM" = "xyes"; then
++    HAVE_KMS="yes"
++  else
++    HAVE_KMS="no"
++  fi
+ ])
+ dnl *** ladspa ***
+diff --git a/ext/wayland/Makefile.am b/ext/wayland/Makefile.am
+index 8442dbd..0d05a5d 100644
+--- a/ext/wayland/Makefile.am
++++ b/ext/wayland/Makefile.am
+@@ -23,13 +23,15 @@ libgstwaylandsink_la_CFLAGS = \
+       $(GST_PLUGINS_BAD_CFLAGS) \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(GST_CFLAGS) \
+-      $(WAYLAND_CFLAGS)
++      $(WAYLAND_CFLAGS) \
++      $(DRM_CFLAGS)
+ libgstwaylandsink_la_LIBADD = \
+       $(top_builddir)/gst-libs/gst/wayland/libgstwayland-$(GST_API_VERSION).la \
+       $(GST_PLUGINS_BASE_LIBS) \
+       -lgstvideo-$(GST_API_VERSION) \
+       -lgstallocators-$(GST_API_VERSION) \
+-      $(WAYLAND_LIBS)
++      $(WAYLAND_LIBS) \
++      $(DRM_LIBS)
+ libgstwaylandsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+ libgstwaylandsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+diff --git a/sys/kms/Makefile.am b/sys/kms/Makefile.am
+index a97cad1..839e560 100644
+--- a/sys/kms/Makefile.am
++++ b/sys/kms/Makefile.am
+@@ -13,7 +13,7 @@ libgstkms_la_CFLAGS =                        \
+       $(GST_VIDEO_CFLAGS)                     \
+       $(GST_ALLOCATORS_CFLAGS)                \
+       $(GST_CFLAGS)                           \
+-      $(KMS_DRM_CFLAGS)                       \
++      $(DRM_CFLAGS)                           \
+       $(NULL)
+ libgstkms_la_LIBADD =                         \
+@@ -22,7 +22,7 @@ libgstkms_la_LIBADD =                        \
+       $(GST_VIDEO_LIBS)                       \
+       $(GST_ALLOCATORS_LIBS)                  \
+       $(GST_LIBS)                             \
+-      $(KMS_DRM_LIBS)                         \
++      $(DRM_LIBS)                             \
+       $(NULL)
+ libgstkms_la_LDFLAGS =                        \
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0004-configure-Factor-out-gudev-checks.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0004-configure-Factor-out-gudev-checks.patch
new file mode 100644 (file)
index 0000000..75fd05e
--- /dev/null
@@ -0,0 +1,41 @@
+From c3d798ee260eb3e8fbef9066c880bed3e86e633b Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 10 May 2017 00:25:07 +0200
+Subject: [PATCH 4/5] configure: Factor out gudev checks
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782922
+---
+ configure.ac | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 3a5e023..62a81b8 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -565,6 +565,12 @@ if test "x$HAVE_WINSOCK2_H" = "xyes"; then
+   AC_SUBST(WINSOCK2_LIBS)
+ fi
++dnl check for gudev
++PKG_CHECK_MODULES(G_UDEV, gudev-1.0 , [
++        AC_DEFINE([HAVE_GUDEV], 1, [Define if gudev is installed])
++        HAVE_GUDEV="yes" ],
++        [HAVE_GUDEV="no"])
++
+ dnl check for libdrm
+ PKG_CHECK_MODULES(DRM, libdrm >= 2.4.55, HAVE_DRM=yes, HAVE_DRM=no)
+@@ -1897,10 +1903,6 @@ dnl *** UVC H264 ***
+ translit(dnm, m, l) AM_CONDITIONAL(USE_UVCH264, true)
+ AG_GST_CHECK_FEATURE(UVCH264, [UVC H264], uvch264, [
+   AC_CHECK_HEADER(linux/uvcvideo.h, HAVE_UVCVIDEO_H=yes, HAVE_UVCVIDEO_H=no)
+-  PKG_CHECK_MODULES(G_UDEV, gudev-1.0 , [
+-          AC_DEFINE([HAVE_GUDEV], 1, [Define if gudev is installed])
+-          HAVE_GUDEV="yes" ],
+-          [HAVE_GUDEV="no"])
+   PKG_CHECK_MODULES(LIBUSB, libusb-1.0 , [
+           AC_DEFINE([HAVE_LIBUSB], 1, [Define if libusb 1.x is installed])
+           HAVE_LIBUSB="yes" ],
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0005-gl-Add-Mesa3D-GBM-backend.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0005-gl-Add-Mesa3D-GBM-backend.patch
new file mode 100644 (file)
index 0000000..f045051
--- /dev/null
@@ -0,0 +1,1833 @@
+From 246b92374f34472c1a936d9a1fb8ce67e596eef5 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 10 May 2017 00:47:27 +0200
+Subject: [PATCH 5/5] gl: Add Mesa3D GBM backend
+
+This makes it possible to use the GStreamer OpenGL elements without a
+windowing system if a libdrm- and Mesa3D-supported GPU is present
+
+Note that the set_window_handle() and set_render_rectangle() vmethods
+are currently not working since it is unclear how these should work
+
+https://bugzilla.gnome.org/show_bug.cgi?id=782923
+---
+ configure.ac                              |  38 +-
+ gst-libs/gst/gl/Makefile.am               |   5 +
+ gst-libs/gst/gl/gbm/Makefile.am           |  31 ++
+ gst-libs/gst/gl/gbm/gstgl_gbm_private.c   | 785 ++++++++++++++++++++++++++++++
+ gst-libs/gst/gl/gbm/gstgl_gbm_private.h   |  77 +++
+ gst-libs/gst/gl/gbm/gstgldisplay_gbm.c    | 156 ++++++
+ gst-libs/gst/gl/gbm/gstgldisplay_gbm.h    |  68 +++
+ gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.c | 367 ++++++++++++++
+ gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.h |  64 +++
+ gst-libs/gst/gl/gstgldisplay.c            |   8 +
+ gst-libs/gst/gl/gstgldisplay.h            |   2 +
+ gst-libs/gst/gl/gstglwindow.c             |   7 +
+ 12 files changed, 1604 insertions(+), 4 deletions(-)
+ create mode 100644 gst-libs/gst/gl/gbm/Makefile.am
+ create mode 100644 gst-libs/gst/gl/gbm/gstgl_gbm_private.c
+ create mode 100644 gst-libs/gst/gl/gbm/gstgl_gbm_private.h
+ create mode 100644 gst-libs/gst/gl/gbm/gstgldisplay_gbm.c
+ create mode 100644 gst-libs/gst/gl/gbm/gstgldisplay_gbm.h
+ create mode 100644 gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.c
+ create mode 100644 gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.h
+
+diff --git a/configure.ac b/configure.ac
+index 62a81b8..9c789d0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -683,6 +683,7 @@ HAVE_GLES2=no
+ HAVE_GLES3_H=no
+ HAVE_WAYLAND_EGL=no
+ HAVE_VIV_FB_EGL=no
++HAVE_GBM_EGL=no
+ HAVE_EGL_RPI=no
+ case $host in
+@@ -724,6 +725,12 @@ case $host in
+         AC_CHECK_LIB([EGL], [fbGetDisplay], [HAVE_VIV_FB_EGL=yes])
+     fi
++    if test "x$HAVE_EGL" = "xyes" -a "x$HAVE_DRM" = "xyes"; then
++        PKG_CHECK_MODULES(GBM, gbm, HAVE_GBM_EGL=yes, HAVE_GBM_EGL=no)
++        AC_SUBST(GBM_CFLAGS)
++        AC_SUBST(GBM_LIBS)
++    fi
++
+     dnl FIXME: Mali EGL depends on GLESv1 or GLESv2
+     AC_CHECK_HEADER([EGL/fbdev_window.h],
+       [
+@@ -997,9 +1004,11 @@ case $host in
+       AC_MSG_ERROR([WGL is not available on unix])
+     fi
+-    if test "x$HAVE_X11_XCB" = "xno"; then
+-      if test "x$HAVE_WAYLAND_EGL" = "xno"; then
+-        AC_MSG_WARN([X or Wayland is required for OpenGL support])
++    if test "x$HAVE_VIV_FB_EGL" = "xno" -o "x$HAVE_GBM_EGL" = "xno"; then
++      if test "x$HAVE_X11_XCB" = "xno"; then
++        if test "x$HAVE_WAYLAND_EGL" = "xno"; then
++          AC_MSG_WARN([X or Wayland is required for OpenGL support])
++        fi
+       fi
+     fi
+@@ -1018,6 +1027,18 @@ case $host in
+       fi
+     fi
++    dnl check Mesa GBM *before* X or Wayland since it is
++    dnl possible that both GBM and X/Wayland are present
++    if test "x$HAVE_GBM_EGL" = "xyes"; then
++      if test "x$NEED_EGL" = "xno" -o "x$HAVE_EGL" = "xno"; then
++        AC_MSG_WARN([EGL is required by the Mesa GBM EGL backend])
++      else
++        HAVE_WINDOW_GBM=yes
++        GL_LIBS="$GL_LIBS"
++        GL_CFLAGS="$GL_CFLAGS"
++      fi
++    fi
++
+     if test "x$HAVE_X11_XCB" = "xyes" -a "x$HAVE_EGL_RPI" = "xno"; then
+       if test "x$NEED_X11" != "xno"; then
+         GL_LIBS="$GL_LIBS $X11_XCB_LIBS"
+@@ -1062,7 +1083,7 @@ case $host in
+       fi
+     else
+       if test "x$NEED_EGL" != "xno"; then
+-        if test "x$HAVE_WINDOW_WAYLAND" = "xyes" -o "x$HAVE_WINDOW_X11" = "xyes" -o "x$HAVE_WINDOW_DISPMANX" = "xyes" -o "x$HAVE_WINDOW_VIV_FB" = "xyes"; then
++        if test "x$HAVE_WINDOW_WAYLAND" = "xyes" -o "x$HAVE_WINDOW_X11" = "xyes" -o "x$HAVE_WINDOW_DISPMANX" = "xyes" -o "x$HAVE_WINDOW_VIV_FB" = "xyes" -o "x$HAVE_WINDOW_GBM" = "xyes"; then
+           GL_LIBS="$GL_LIBS -lEGL $EGL_LIBS"
+           GL_CFLAGS="$GL_CFLAGS $EGL_CFLAGS"
+           USE_EGL=yes
+@@ -1215,6 +1236,7 @@ GST_GL_HAVE_WINDOW_ANDROID=0
+ GST_GL_HAVE_WINDOW_DISPMANX=0
+ GST_GL_HAVE_WINDOW_EAGL=0
+ GST_GL_HAVE_WINDOW_VIV_FB=0
++GST_GL_HAVE_WINDOW_GBM=0
+ if test "x$HAVE_WINDOW_X11" = "xyes"; then
+   GL_WINDOWS="x11 $GL_WINDOWS"
+@@ -1248,6 +1270,10 @@ if test "x$HAVE_WINDOW_VIV_FB" = "xyes"; then
+   GL_WINDOWS="viv-fb $GL_WINDOWS"
+   GST_GL_HAVE_WINDOW_VIV_FB=1
+ fi
++if test "x$HAVE_WINDOW_GBM" = "xyes"; then
++  GL_WINDOWS="gbm $GL_WINDOWS"
++  GST_GL_HAVE_WINDOW_GBM=1
++fi
+ GL_CONFIG_DEFINES="$GL_CONFIG_DEFINES
+ #define GST_GL_HAVE_WINDOW_X11 $GST_GL_HAVE_WINDOW_X11
+@@ -1258,6 +1284,7 @@ GL_CONFIG_DEFINES="$GL_CONFIG_DEFINES
+ #define GST_GL_HAVE_WINDOW_DISPMANX $GST_GL_HAVE_WINDOW_DISPMANX
+ #define GST_GL_HAVE_WINDOW_EAGL $GST_GL_HAVE_WINDOW_EAGL
+ #define GST_GL_HAVE_WINDOW_VIV_FB $GST_GL_HAVE_WINDOW_VIV_FB
++#define GST_GL_HAVE_WINDOW_GBM $GST_GL_HAVE_WINDOW_GBM
+ "
+ dnl PLATFORM's
+@@ -1335,6 +1362,7 @@ if test "x$GL_APIS" = "x" -o "x$GL_PLATFORMS" = "x" -o "x$GL_WINDOWS" = "x"; the
+   HAVE_WINDOW_COCOA=no
+   HAVE_WINDOW_EAGL=no
+   HAVE_WINDOW_VIV_FB=no
++  HAVE_WINDOW_GBM=no
+ fi
+ AC_SUBST(GL_APIS)
+@@ -1354,6 +1382,7 @@ AM_CONDITIONAL(HAVE_WINDOW_WAYLAND, test "x$HAVE_WINDOW_WAYLAND" = "xyes")
+ AM_CONDITIONAL(HAVE_WINDOW_ANDROID, test "x$HAVE_WINDOW_ANDROID" = "xyes")
+ AM_CONDITIONAL(HAVE_WINDOW_EAGL, test "x$HAVE_WINDOW_EAGL" = "xyes")
+ AM_CONDITIONAL(HAVE_WINDOW_VIV_FB, test "x$HAVE_WINDOW_VIV_FB" = "xyes")
++AM_CONDITIONAL(HAVE_WINDOW_GBM, test "x$HAVE_WINDOW_GBM" = "xyes")
+ AM_CONDITIONAL(USE_OPENGL, test "x$USE_OPENGL" = "xyes")
+ AM_CONDITIONAL(USE_GLES2, test "x$USE_GLES2" = "xyes")
+@@ -3602,6 +3631,7 @@ gst-libs/gst/gl/wayland/Makefile
+ gst-libs/gst/gl/win32/Makefile
+ gst-libs/gst/gl/x11/Makefile
+ gst-libs/gst/gl/viv-fb/Makefile
++gst-libs/gst/gl/gbm/Makefile
+ gst-libs/gst/insertbin/Makefile
+ gst-libs/gst/interfaces/Makefile
+ gst-libs/gst/codecparsers/Makefile
+diff --git a/gst-libs/gst/gl/Makefile.am b/gst-libs/gst/gl/Makefile.am
+index dfa7a7d..fecee98 100644
+--- a/gst-libs/gst/gl/Makefile.am
++++ b/gst-libs/gst/gl/Makefile.am
+@@ -125,6 +125,11 @@ SUBDIRS += viv-fb
+ libgstgl_@GST_API_VERSION@_la_LIBADD += viv-fb/libgstgl-viv-fb.la
+ endif
++if HAVE_WINDOW_GBM
++SUBDIRS += gbm
++libgstgl_@GST_API_VERSION@_la_LIBADD += gbm/libgstgl-gbm.la
++endif
++
+ if USE_EGL
+ SUBDIRS += egl
+ libgstgl_@GST_API_VERSION@_la_LIBADD += egl/libgstgl-egl.la
+diff --git a/gst-libs/gst/gl/gbm/Makefile.am b/gst-libs/gst/gl/gbm/Makefile.am
+new file mode 100644
+index 0000000..cdded4c
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/Makefile.am
+@@ -0,0 +1,31 @@
++## Process this file with automake to produce Makefile.in
++
++noinst_LTLIBRARIES = libgstgl-gbm.la
++
++libgstgl_gbm_la_SOURCES = \
++      gstgl_gbm_private.c \
++      gstgldisplay_gbm.c \
++      gstglwindow_gbm_egl.c
++
++noinst_HEADERS = \
++      gstgl_gbm_private.h \
++      gstgldisplay_gbm.h \
++      gstglwindow_gbm_egl.h
++
++libgstgl_gbm_la_CFLAGS = \
++      -I$(top_srcdir)/gst-libs \
++      -I$(top_builddir)/gst-libs \
++      $(GL_CFLAGS) \
++      $(GST_PLUGINS_BASE_CFLAGS) \
++      $(GST_BASE_CFLAGS) \
++      $(GST_CFLAGS) \
++      $(DRM_CFLAGS) \
++      $(GBM_CFLAGS) \
++      $(G_UDEV_CFLAGS)
++
++libgstgl_gbm_la_LDFLAGS = \
++      $(GST_LIB_LDFLAGS) \
++      $(GST_ALL_LDFLAGS) \
++      $(DRM_LIBS) \
++      $(GBM_LIBS) \
++      $(G_UDEV_LIBS)
+diff --git a/gst-libs/gst/gl/gbm/gstgl_gbm_private.c b/gst-libs/gst/gl/gbm/gstgl_gbm_private.c
+new file mode 100644
+index 0000000..596c853
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstgl_gbm_private.c
+@@ -0,0 +1,785 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include <inttypes.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <gudev/gudev.h>
++#include "gstgl_gbm_private.h"
++
++GST_DEBUG_CATEGORY_EXTERN (gst_gl_gbm_debug);
++#define GST_CAT_DEFAULT gst_gl_gbm_debug
++
++
++#define INVALID_CRTC ((uint32_t)0)
++
++
++static const gchar *
++get_name_for_drm_connector (drmModeConnector * connector)
++{
++  g_assert (connector != NULL);
++
++  switch (connector->connector_type) {
++    case DRM_MODE_CONNECTOR_Unknown:
++      return "Unknown";
++    case DRM_MODE_CONNECTOR_VGA:
++      return "VGA";
++    case DRM_MODE_CONNECTOR_DVII:
++      return "DVI-I";
++    case DRM_MODE_CONNECTOR_DVID:
++      return "DVI-D";
++    case DRM_MODE_CONNECTOR_DVIA:
++      return "DVI-A";
++    case DRM_MODE_CONNECTOR_Composite:
++      return "Composite";
++    case DRM_MODE_CONNECTOR_SVIDEO:
++      return "S-Video";
++    case DRM_MODE_CONNECTOR_LVDS:
++      return "LVDS";
++    case DRM_MODE_CONNECTOR_Component:
++      return "Component";
++    case DRM_MODE_CONNECTOR_9PinDIN:
++      return "9-Pin DIN";
++    case DRM_MODE_CONNECTOR_DisplayPort:
++      return "DisplayPort";
++    case DRM_MODE_CONNECTOR_HDMIA:
++      return "HDMI-A";
++    case DRM_MODE_CONNECTOR_HDMIB:
++      return "HDMI-B";
++    case DRM_MODE_CONNECTOR_TV:
++      return "TV";
++    case DRM_MODE_CONNECTOR_eDP:
++      return "eDP";
++    case DRM_MODE_CONNECTOR_VIRTUAL:
++      return "Virtual";
++    case DRM_MODE_CONNECTOR_DSI:
++      return "DSI";
++    default:
++      return "<unknown>";
++  }
++}
++
++
++static const gchar *
++get_name_for_drm_encoder (drmModeEncoder * encoder)
++{
++  switch (encoder->encoder_type) {
++    case DRM_MODE_ENCODER_NONE:
++      return "none";
++    case DRM_MODE_ENCODER_DAC:
++      return "DAC";
++    case DRM_MODE_ENCODER_TMDS:
++      return "TMDS";
++    case DRM_MODE_ENCODER_LVDS:
++      return "LVDS";
++    case DRM_MODE_ENCODER_TVDAC:
++      return "TVDAC";
++    case DRM_MODE_ENCODER_VIRTUAL:
++      return "Virtual";
++    case DRM_MODE_ENCODER_DSI:
++      return "DSI";
++    default:
++      return "<unknown>";
++  }
++}
++
++
++static uint32_t
++find_crtc_id_for_encoder (GstGLDisplayGBMPrivate * priv,
++    drmModeEncoder const *encoder)
++{
++  int i;
++  for (i = 0; i < priv->drm_mode_resources->count_crtcs; ++i) {
++    /* possible_crtcs is a bitmask as described here:
++     * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api */
++    uint32_t const crtc_mask = 1 << i;
++    uint32_t const crtc_id = priv->drm_mode_resources->crtcs[i];
++
++    if (encoder->possible_crtcs & crtc_mask)
++      return crtc_id;
++  }
++
++  // No match found
++  return INVALID_CRTC;
++}
++
++
++static uint32_t
++find_crtc_id_for_connector (GstGLDisplayGBMPrivate * priv)
++{
++  int i;
++  for (i = 0; i < priv->drm_mode_connector->count_encoders; ++i) {
++    uint32_t encoder_id = priv->drm_mode_connector->encoders[i];
++    drmModeEncoder *encoder = drmModeGetEncoder (priv->drm_fd, encoder_id);
++
++    if (encoder != NULL) {
++      uint32_t crtc_id = find_crtc_id_for_encoder (priv, encoder);
++      drmModeFreeEncoder (encoder);
++
++      if (crtc_id != INVALID_CRTC)
++        return crtc_id;
++    }
++  }
++
++  // No match found
++  return INVALID_CRTC;
++}
++
++
++const gchar *
++gst_gl_gbm_format_to_string (uint32_t format)
++{
++  if (format == GBM_BO_FORMAT_XRGB8888)
++    format = GBM_FORMAT_XRGB8888;
++  if (format == GBM_BO_FORMAT_ARGB8888)
++    format = GBM_FORMAT_ARGB8888;
++
++  switch (format) {
++    case GBM_FORMAT_C8:
++      return "C8";
++    case GBM_FORMAT_RGB332:
++      return "RGB332";
++    case GBM_FORMAT_BGR233:
++      return "BGR233";
++    case GBM_FORMAT_NV12:
++      return "NV12";
++    case GBM_FORMAT_XRGB4444:
++      return "XRGB4444";
++    case GBM_FORMAT_XBGR4444:
++      return "XBGR4444";
++    case GBM_FORMAT_RGBX4444:
++      return "RGBX4444";
++    case GBM_FORMAT_BGRX4444:
++      return "BGRX4444";
++    case GBM_FORMAT_XRGB1555:
++      return "XRGB1555";
++    case GBM_FORMAT_XBGR1555:
++      return "XBGR1555";
++    case GBM_FORMAT_RGBX5551:
++      return "RGBX5551";
++    case GBM_FORMAT_BGRX5551:
++      return "BGRX5551";
++    case GBM_FORMAT_ARGB4444:
++      return "ARGB4444";
++    case GBM_FORMAT_ABGR4444:
++      return "ABGR4444";
++    case GBM_FORMAT_RGBA4444:
++      return "RGBA4444";
++    case GBM_FORMAT_BGRA4444:
++      return "BGRA4444";
++    case GBM_FORMAT_ARGB1555:
++      return "ARGB1555";
++    case GBM_FORMAT_ABGR1555:
++      return "ABGR1555";
++    case GBM_FORMAT_RGBA5551:
++      return "RGBA5551";
++    case GBM_FORMAT_BGRA5551:
++      return "BGRA5551";
++    case GBM_FORMAT_RGB565:
++      return "RGB565";
++    case GBM_FORMAT_BGR565:
++      return "BGR565";
++    case GBM_FORMAT_YUYV:
++      return "YUYV";
++    case GBM_FORMAT_YVYU:
++      return "YVYU";
++    case GBM_FORMAT_UYVY:
++      return "UYVY";
++    case GBM_FORMAT_VYUY:
++      return "VYUY";
++    case GBM_FORMAT_RGB888:
++      return "RGB888";
++    case GBM_FORMAT_BGR888:
++      return "BGR888";
++    case GBM_FORMAT_XRGB8888:
++      return "XRGB8888";
++    case GBM_FORMAT_XBGR8888:
++      return "XBGR8888";
++    case GBM_FORMAT_RGBX8888:
++      return "RGBX8888";
++    case GBM_FORMAT_BGRX8888:
++      return "BGRX8888";
++    case GBM_FORMAT_AYUV:
++      return "AYUV";
++    case GBM_FORMAT_XRGB2101010:
++      return "XRGB2101010";
++    case GBM_FORMAT_XBGR2101010:
++      return "XBGR2101010";
++    case GBM_FORMAT_RGBX1010102:
++      return "RGBX1010102";
++    case GBM_FORMAT_BGRX1010102:
++      return "BGRX1010102";
++    case GBM_FORMAT_ARGB8888:
++      return "ARGB8888";
++    case GBM_FORMAT_ABGR8888:
++      return "ABGR8888";
++    case GBM_FORMAT_RGBA8888:
++      return "RGBA8888";
++    case GBM_FORMAT_BGRA8888:
++      return "BGRA8888";
++    case GBM_FORMAT_ARGB2101010:
++      return "ARGB2101010";
++    case GBM_FORMAT_ABGR2101010:
++      return "ABGR2101010";
++    case GBM_FORMAT_RGBA1010102:
++      return "RGBA1010102";
++    case GBM_FORMAT_BGRA1010102:
++      return "BGRA1010102";
++
++    default:
++      return "<unknown>";
++  }
++
++  return NULL;
++}
++
++
++int
++gst_gl_gbm_depth_from_format (uint32_t format)
++{
++  if (format == GBM_BO_FORMAT_XRGB8888)
++    format = GBM_FORMAT_XRGB8888;
++  if (format == GBM_BO_FORMAT_ARGB8888)
++    format = GBM_FORMAT_ARGB8888;
++
++  switch (format) {
++    case GBM_FORMAT_C8:
++    case GBM_FORMAT_RGB332:
++    case GBM_FORMAT_BGR233:
++      return 8;
++
++    case GBM_FORMAT_NV12:
++    case GBM_FORMAT_XRGB4444:
++    case GBM_FORMAT_XBGR4444:
++    case GBM_FORMAT_RGBX4444:
++    case GBM_FORMAT_BGRX4444:
++      return 12;
++
++    case GBM_FORMAT_XRGB1555:
++    case GBM_FORMAT_XBGR1555:
++    case GBM_FORMAT_RGBX5551:
++    case GBM_FORMAT_BGRX5551:
++      return 15;
++
++    case GBM_FORMAT_ARGB4444:
++    case GBM_FORMAT_ABGR4444:
++    case GBM_FORMAT_RGBA4444:
++    case GBM_FORMAT_BGRA4444:
++    case GBM_FORMAT_ARGB1555:
++    case GBM_FORMAT_ABGR1555:
++    case GBM_FORMAT_RGBA5551:
++    case GBM_FORMAT_BGRA5551:
++    case GBM_FORMAT_RGB565:
++    case GBM_FORMAT_BGR565:
++    case GBM_FORMAT_YUYV:
++    case GBM_FORMAT_YVYU:
++    case GBM_FORMAT_UYVY:
++    case GBM_FORMAT_VYUY:
++      return 16;
++
++    case GBM_FORMAT_RGB888:
++    case GBM_FORMAT_BGR888:
++    case GBM_FORMAT_XRGB8888:
++    case GBM_FORMAT_XBGR8888:
++    case GBM_FORMAT_RGBX8888:
++    case GBM_FORMAT_BGRX8888:
++    case GBM_FORMAT_AYUV:
++      return 24;
++
++    case GBM_FORMAT_XRGB2101010:
++    case GBM_FORMAT_XBGR2101010:
++    case GBM_FORMAT_RGBX1010102:
++    case GBM_FORMAT_BGRX1010102:
++      return 30;
++
++    case GBM_FORMAT_ARGB8888:
++    case GBM_FORMAT_ABGR8888:
++    case GBM_FORMAT_RGBA8888:
++    case GBM_FORMAT_BGRA8888:
++    case GBM_FORMAT_ARGB2101010:
++    case GBM_FORMAT_ABGR2101010:
++    case GBM_FORMAT_RGBA1010102:
++    case GBM_FORMAT_BGRA1010102:
++      return 32;
++
++    default:
++      GST_ERROR ("unknown GBM format %" PRIu32, format);
++  }
++
++  return 0;
++}
++
++
++int
++gst_gl_gbm_bpp_from_format (uint32_t format)
++{
++  if (format == GBM_BO_FORMAT_XRGB8888)
++    format = GBM_FORMAT_XRGB8888;
++  if (format == GBM_BO_FORMAT_ARGB8888)
++    format = GBM_FORMAT_ARGB8888;
++
++  switch (format) {
++    case GBM_FORMAT_C8:
++    case GBM_FORMAT_RGB332:
++    case GBM_FORMAT_BGR233:
++      return 8;
++
++    case GBM_FORMAT_NV12:
++      return 12;
++
++    case GBM_FORMAT_XRGB4444:
++    case GBM_FORMAT_XBGR4444:
++    case GBM_FORMAT_RGBX4444:
++    case GBM_FORMAT_BGRX4444:
++    case GBM_FORMAT_ARGB4444:
++    case GBM_FORMAT_ABGR4444:
++    case GBM_FORMAT_RGBA4444:
++    case GBM_FORMAT_BGRA4444:
++    case GBM_FORMAT_XRGB1555:
++    case GBM_FORMAT_XBGR1555:
++    case GBM_FORMAT_RGBX5551:
++    case GBM_FORMAT_BGRX5551:
++    case GBM_FORMAT_ARGB1555:
++    case GBM_FORMAT_ABGR1555:
++    case GBM_FORMAT_RGBA5551:
++    case GBM_FORMAT_BGRA5551:
++    case GBM_FORMAT_RGB565:
++    case GBM_FORMAT_BGR565:
++    case GBM_FORMAT_YUYV:
++    case GBM_FORMAT_YVYU:
++    case GBM_FORMAT_UYVY:
++    case GBM_FORMAT_VYUY:
++      return 16;
++
++    case GBM_FORMAT_RGB888:
++    case GBM_FORMAT_BGR888:
++      return 24;
++
++    case GBM_FORMAT_XRGB8888:
++    case GBM_FORMAT_XBGR8888:
++    case GBM_FORMAT_RGBX8888:
++    case GBM_FORMAT_BGRX8888:
++    case GBM_FORMAT_ARGB8888:
++    case GBM_FORMAT_ABGR8888:
++    case GBM_FORMAT_RGBA8888:
++    case GBM_FORMAT_BGRA8888:
++    case GBM_FORMAT_XRGB2101010:
++    case GBM_FORMAT_XBGR2101010:
++    case GBM_FORMAT_RGBX1010102:
++    case GBM_FORMAT_BGRX1010102:
++    case GBM_FORMAT_ARGB2101010:
++    case GBM_FORMAT_ABGR2101010:
++    case GBM_FORMAT_RGBA1010102:
++    case GBM_FORMAT_BGRA1010102:
++    case GBM_FORMAT_AYUV:
++      return 32;
++
++    default:
++      GST_ERROR ("unknown GBM format %" PRIu32, format);
++  }
++
++  return 0;
++}
++
++
++static void
++gst_gl_gbm_drm_fb_destroy_callback (struct gbm_bo *bo, void *data)
++{
++  int drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
++  GstGLDRMFramebuffer *fb = (GstGLDRMFramebuffer *) (data);
++
++  if (fb->fb_id)
++    drmModeRmFB (drm_fd, fb->fb_id);
++
++  g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
++}
++
++
++GstGLDRMFramebuffer *
++gst_gl_gbm_drm_fb_get_from_bo (struct gbm_bo *bo)
++{
++  GstGLDRMFramebuffer *fb;
++  int drm_fd;
++  uint32_t width, height, stride, format, handle;
++  int depth, bpp;
++  int ret;
++
++  /* We want to use this buffer object (abbr. "bo") as a scanout buffer.
++   * To that end, we associate the bo with the DRM by using drmModeAddFB().
++   * However, this needs to be called only once, and the counterpart,
++   * drmModeRmFB(), needs to be called when the bo is cleaned up.
++   *
++   * To fulfill these requirements, add extra framebuffer information to the
++   * bo as "user data". This way, if this user data pointer is NULL, it means
++   * that no framebuffer information was generated yet & the bo was not set
++   * as a scanout buffer with drmModeAddFB() yet, and we have perform these
++   * steps. Otherwise, if it is non-NULL, we know we do not have to set up
++   * anything (since it was done already) and just return the pointer to the
++   * framebuffer information. */
++  fb = (GstGLDRMFramebuffer *) (gbm_bo_get_user_data (bo));
++  if (fb != NULL) {
++    /* The bo was already set up as a scanout framebuffer. Just
++     * return the framebuffer information. */
++    return fb;
++  }
++
++  /* If this point is reached, then we have to setup the bo as a
++   * scanout framebuffer. */
++
++  drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
++
++  fb = g_slice_alloc0 (sizeof (GstGLDRMFramebuffer));
++  fb->bo = bo;
++
++  width = gbm_bo_get_width (bo);
++  height = gbm_bo_get_height (bo);
++  stride = gbm_bo_get_stride (bo);
++  format = gbm_bo_get_format (bo);
++  handle = gbm_bo_get_handle (bo).u32;
++
++  depth = gst_gl_gbm_depth_from_format (format);
++  bpp = gst_gl_gbm_bpp_from_format (format);
++
++  GST_DEBUG ("Attempting to add GBM BO as scanout framebuffer width/height: %"
++      PRIu32 "/%" PRIu32 " pixels  stride: %" PRIu32 " bytes  format: %s "
++      " depth: %d bits  total bpp: %d bits", width, height, stride,
++      gst_gl_gbm_format_to_string (format), depth, bpp);
++
++  /* Set the bo as a scanout framebuffer */
++  ret = drmModeAddFB (drm_fd, width, height, depth, bpp, stride, handle,
++      &fb->fb_id);
++  if (ret != 0) {
++    GST_ERROR ("Failed to add GBM BO as scanout framebuffer: %s (%d)",
++        g_strerror (errno), errno);
++    g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
++    return NULL;
++  }
++
++  /* Add the framebuffer information to the bo as user data, and also install a callback
++   * that cleans up this extra information whenever the bo itself is discarded */
++  gbm_bo_set_user_data (bo, fb, gst_gl_gbm_drm_fb_destroy_callback);
++
++  return fb;
++}
++
++
++int
++gst_gl_gbm_find_and_open_drm_node (void)
++{
++  /* In here we use GUDev to try to autodetect the GPU */
++
++  int drm_fd = -1;
++  GUdevClient *gudev_client = NULL;
++  GUdevEnumerator *gudev_enum = NULL;
++  GList *devlist = NULL;
++  GList *deventry = NULL;
++  const gchar *subsystems[2] = { "drm", NULL };
++
++  gudev_client = g_udev_client_new (subsystems);
++  if (gudev_client == NULL) {
++    GST_ERROR ("Could not create gudev client");
++    goto cleanup;
++  }
++  GST_DEBUG ("Created gudev client");
++
++  gudev_enum = g_udev_enumerator_new (gudev_client);
++  if (gudev_enum == NULL) {
++    GST_ERROR ("Could not create gudev enumerator");
++    goto cleanup;
++  }
++  GST_DEBUG ("Created gudev enumerator");
++
++  /* TODO: To be 100% sure we pick the right device, also check
++   * if this is a GPU, because a pure scanout device could also
++   * have a DRM subsystem for example. However, currently it is
++   * unclear how to do that. By trying to create an EGL context? */
++  g_udev_enumerator_add_match_subsystem (gudev_enum, "drm");
++  devlist = g_udev_enumerator_execute (gudev_enum);
++  GST_DEBUG ("Scanned for udev devices with a drm subsytem");
++
++  if (devlist == NULL) {
++    GST_WARNING ("Found no matching DRM devices");
++    goto cleanup;
++  }
++  GST_DEBUG ("Got %u potentially matching device(s)", g_list_length (devlist));
++
++  for (deventry = devlist; deventry != NULL; deventry = deventry->next) {
++    GUdevDevice *gudevice = G_UDEV_DEVICE (deventry->data);
++    const gchar *devnode = g_udev_device_get_device_file (gudevice);
++
++    if (!g_str_has_prefix (devnode, "/dev/dri/card"))
++      continue;
++
++    GST_DEBUG ("Found DRM device with device node \"%s\"", devnode);
++
++    drm_fd = open (devnode, O_RDWR | O_CLOEXEC);
++    if (drm_fd < 0) {
++      GST_WARNING ("Cannot open device node \"%s\": %s (%d)", devnode,
++          g_strerror (errno), errno);
++      continue;
++    }
++
++    GST_DEBUG ("Device node \"%s\" is a valid DRM device node", devnode);
++    break;
++  }
++
++
++done:
++
++  if (devlist != NULL) {
++    g_list_free_full (devlist, g_object_unref);
++    devlist = NULL;
++    GST_DEBUG ("Cleaned up device list");
++  }
++
++  if (gudev_enum != NULL) {
++    g_object_unref (G_OBJECT (gudev_enum));
++    gudev_enum = NULL;
++    GST_DEBUG ("Cleaned up gudev enumerator");
++  }
++
++  if (gudev_client != NULL) {
++    g_object_unref (G_OBJECT (gudev_client));
++    gudev_client = NULL;
++    GST_DEBUG ("Cleaned up gudev client");
++  }
++
++  return drm_fd;
++
++
++cleanup:
++
++  if (drm_fd >= 0) {
++    close (drm_fd);
++    drm_fd = -1;
++  }
++
++  goto done;
++}
++
++
++gboolean
++gst_gl_display_gbm_setup_drm (GstGLDisplayGBMPrivate * priv)
++{
++  int i;
++
++  g_assert (priv != NULL);
++  g_assert (priv->drm_fd >= 0);
++
++  /* Get the DRM mode resources */
++  priv->drm_mode_resources = drmModeGetResources (priv->drm_fd);
++  if (priv->drm_mode_resources == NULL) {
++    GST_ERROR ("Could not get DRM resources: %s (%d)", g_strerror (errno),
++        errno);
++    goto cleanup;
++  }
++  GST_DEBUG ("Got DRM resources");
++
++  /*Find a connected connector. The connector is where the pixel data is
++   * finally sent to, and typically connects to some form of display, like an
++   * HDMI TV, an LVDS panel etc. */
++  {
++    drmModeConnector *connector = NULL;
++
++    GST_DEBUG ("Checking %d DRM connector(s)",
++        priv->drm_mode_resources->count_connectors);
++    for (i = 0; i < priv->drm_mode_resources->count_connectors; ++i) {
++      connector = drmModeGetConnector (priv->drm_fd,
++          priv->drm_mode_resources->connectors[i]);
++      GST_DEBUG ("Found DRM connector #%d \"%s\" with ID %" PRIu32, i,
++          get_name_for_drm_connector (connector), connector->connector_id);
++
++      if (connector->connection == DRM_MODE_CONNECTED) {
++        GST_DEBUG ("DRM connector #%d is connected", i);
++        break;
++      }
++
++      drmModeFreeConnector (connector);
++      connector = NULL;
++    }
++
++    if (connector == NULL) {
++      GST_ERROR ("No connected DRM connector found");
++      goto cleanup;
++    }
++
++    priv->drm_mode_connector = connector;
++  }
++
++  /* Check out what modes are supported by the chosen connector,
++   * and pick either the "preferred" mode or the one with the largest
++   * pixel area. */
++  {
++    int selected_mode_index = -1;
++    int selected_mode_area = -1;
++
++    GST_DEBUG ("Checking %d DRM mode(s) from selected connector",
++        priv->drm_mode_connector->count_modes);
++    for (i = 0; i < priv->drm_mode_connector->count_modes; ++i) {
++      drmModeModeInfo *current_mode = &(priv->drm_mode_connector->modes[i]);
++      int current_mode_area = current_mode->hdisplay * current_mode->vdisplay;
++
++      GST_DEBUG ("Found DRM mode #%d width/height %" PRIu16 "/%" PRIu16
++          " hsync/vsync start %" PRIu16 "/%" PRIu16 " hsync/vsync end %" PRIu16
++          "/%" PRIu16 " htotal/vtotal %" PRIu16 "/%" PRIu16 " hskew %" PRIu16
++          " vscan %" PRIu16 " vrefresh %" PRIu32 " preferred %d", i,
++          current_mode->hdisplay, current_mode->vdisplay,
++          current_mode->hsync_start, current_mode->vsync_start,
++          current_mode->hsync_end, current_mode->vsync_end,
++          current_mode->htotal, current_mode->vtotal, current_mode->hskew,
++          current_mode->vscan, current_mode->vrefresh,
++          (current_mode->type & DRM_MODE_TYPE_PREFERRED) ? TRUE : FALSE);
++
++      if ((current_mode->type & DRM_MODE_TYPE_PREFERRED) ||
++          (current_mode_area > selected_mode_area)) {
++        priv->drm_mode_info = current_mode;
++        selected_mode_area = current_mode_area;
++        selected_mode_index = i;
++
++        if (current_mode->type & DRM_MODE_TYPE_PREFERRED)
++          break;
++      }
++    }
++
++    if (priv->drm_mode_info == NULL) {
++      GST_ERROR ("No usable DRM mode found");
++      goto cleanup;
++    }
++
++    GST_DEBUG ("Selected DRM mode #%d", selected_mode_index);
++  }
++
++  /* Find an encoder that is attached to the chosen connector. Also find the
++   * index/id of the CRTC associated with this encoder. The encoder takes pixel
++   * data from the CRTC and transmits it to the connector. The CRTC roughly
++   * represents the scanout framebuffer.
++   *
++   * Ultimately, we only care about the CRTC index & ID, so the encoder
++   * reference is discarded here once these are found. The CRTC index is the
++   * index in the m_drm_mode_resources' CRTC array, while the ID is an identifier
++   * used by the DRM to refer to the CRTC universally. (We need the CRTC
++   * information for page flipping and DRM scanout framebuffer configuration.) */
++  {
++    drmModeEncoder *encoder = NULL;
++
++    GST_DEBUG ("Checking %d DRM encoder(s)",
++        priv->drm_mode_resources->count_encoders);
++    for (i = 0; i < priv->drm_mode_resources->count_encoders; ++i) {
++      encoder = drmModeGetEncoder (priv->drm_fd,
++          priv->drm_mode_resources->encoders[i]);
++
++      GST_DEBUG ("Found DRM encoder #%d \"%s\"", i,
++          get_name_for_drm_encoder (encoder));
++
++      if (encoder->encoder_id == priv->drm_mode_connector->encoder_id) {
++        GST_DEBUG ("DRM encoder #%d corresponds to selected DRM connector "
++            "-> selected", i);
++        break;
++      }
++      drmModeFreeEncoder (encoder);
++      encoder = NULL;
++    }
++
++    if (encoder == NULL) {
++      GST_DEBUG ("No encoder found; searching for CRTC ID in the connector");
++      priv->crtc_id = find_crtc_id_for_connector (priv);
++    } else {
++      GST_DEBUG ("Using CRTC ID from selected encoder");
++      priv->crtc_id = encoder->crtc_id;
++      drmModeFreeEncoder (encoder);
++    }
++
++    if (priv->crtc_id == INVALID_CRTC) {
++      GST_ERROR ("No CRTC found");
++      goto cleanup;
++    }
++
++    GST_DEBUG ("CRTC with ID %" PRIu32 " found; now locating it in the DRM "
++        "mode resources CRTC array", priv->crtc_id);
++
++    for (i = 0; i < priv->drm_mode_resources->count_crtcs; ++i) {
++      if (priv->drm_mode_resources->crtcs[i] == priv->crtc_id) {
++        priv->crtc_index = i;
++        break;
++      }
++    }
++
++    if (priv->crtc_index < 0) {
++      GST_ERROR ("No matching CRTC entry in DRM resources found");
++      goto cleanup;
++    }
++
++    GST_DEBUG ("CRTC with ID %" PRIu32 " can be found at index #%d in the DRM "
++        "mode resources CRTC array", priv->crtc_id, priv->crtc_index);
++  }
++
++  GST_DEBUG ("DRM structures initialized");
++  return TRUE;
++
++cleanup:
++  gst_gl_display_gbm_shutdown_drm (priv);
++  return FALSE;
++}
++
++
++void
++gst_gl_display_gbm_shutdown_drm (GstGLDisplayGBMPrivate * priv)
++{
++  g_assert (priv != NULL);
++
++  priv->drm_mode_info = NULL;
++
++  priv->crtc_index = -1;
++  priv->crtc_id = INVALID_CRTC;
++
++  if (priv->drm_mode_connector != NULL) {
++    drmModeFreeConnector (priv->drm_mode_connector);
++    priv->drm_mode_connector = NULL;
++  }
++
++  if (priv->drm_mode_resources != NULL) {
++    drmModeFreeResources (priv->drm_mode_resources);
++    priv->drm_mode_resources = NULL;
++  }
++}
++
++
++gboolean
++gst_gl_display_gbm_setup_gbm (GstGLDisplayGBMPrivate * priv)
++{
++  priv->gbm_dev = gbm_create_device (priv->drm_fd);
++  if (priv->gbm_dev == NULL) {
++    GST_ERROR ("Creating GBM device failed");
++    return FALSE;
++  }
++
++  GST_DEBUG ("GBM structures initialized");
++  return TRUE;
++}
++
++
++void
++gst_gl_display_gbm_shutdown_gbm (GstGLDisplayGBMPrivate * priv)
++{
++  if (priv->gbm_dev != NULL) {
++    gbm_device_destroy (priv->gbm_dev);
++    priv->gbm_dev = NULL;
++  }
++}
+diff --git a/gst-libs/gst/gl/gbm/gstgl_gbm_private.h b/gst-libs/gst/gl/gbm/gstgl_gbm_private.h
+new file mode 100644
+index 0000000..0a85c30
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstgl_gbm_private.h
+@@ -0,0 +1,77 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#ifndef __GST_GL_GBM_PRIVATE_H__
++#define __GST_GL_GBM_PRIVATE_H__
++
++#include <stdint.h>
++#include <gbm.h>
++#include <xf86drm.h>
++#include <xf86drmMode.h>
++#include <gst/gst.h>
++
++
++typedef struct _GstGLDisplayGBMPrivate GstGLDisplayGBMPrivate;
++typedef struct _GstGLWindowGBMEGLPrivate GstGLWindowGBMEGLPrivate;
++typedef struct _GstGLDRMFramebuffer GstGLDRMFramebuffer;
++
++struct _GstGLDisplayGBMPrivate
++{
++  int drm_fd;
++  drmModeRes *drm_mode_resources;
++  drmModeConnector *drm_mode_connector;
++  drmModeModeInfo *drm_mode_info;
++  int crtc_index;
++  uint32_t crtc_id;
++
++  struct gbm_device *gbm_dev;
++};
++
++struct _GstGLWindowGBMEGLPrivate
++{
++  struct gbm_surface *gbm_surf;
++  struct gbm_bo *current_bo;
++
++  GstGLDisplayGBMPrivate *display_priv;
++};
++
++struct _GstGLDRMFramebuffer
++{
++  struct gbm_bo *bo;
++  uint32_t fb_id;
++};
++
++
++const gchar* gst_gl_gbm_format_to_string (uint32_t format);
++int gst_gl_gbm_depth_from_format (uint32_t format);
++int gst_gl_gbm_bpp_from_format (uint32_t format);
++
++GstGLDRMFramebuffer* gst_gl_gbm_drm_fb_get_from_bo (struct gbm_bo *bo);
++
++int gst_gl_gbm_find_and_open_drm_node (void);
++
++gboolean gst_gl_display_gbm_setup_drm (GstGLDisplayGBMPrivate *priv);
++void gst_gl_display_gbm_shutdown_drm (GstGLDisplayGBMPrivate *priv);
++
++gboolean gst_gl_display_gbm_setup_gbm (GstGLDisplayGBMPrivate *priv);
++void gst_gl_display_gbm_shutdown_gbm (GstGLDisplayGBMPrivate *priv);
++
++
++#endif /* __GST_GL_DISPLAY_GBM_PRIVATE_H__ */
+diff --git a/gst-libs/gst/gl/gbm/gstgldisplay_gbm.c b/gst-libs/gst/gl/gbm/gstgldisplay_gbm.c
+new file mode 100644
+index 0000000..968c90a
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstgldisplay_gbm.c
+@@ -0,0 +1,156 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "gstgldisplay_gbm.h"
++#include "gstgl_gbm_private.h"
++
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++
++GST_DEBUG_CATEGORY (gst_gl_gbm_debug);
++
++GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
++#define GST_CAT_DEFAULT gst_gl_display_debug
++
++
++#define GST_GL_DISPLAY_GBM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
++    GST_TYPE_GL_DISPLAY_GBM, GstGLDisplayGBMPrivate))
++
++
++G_DEFINE_TYPE (GstGLDisplayGBM, gst_gl_display_gbm, GST_TYPE_GL_DISPLAY);
++
++
++static void gst_gl_display_gbm_finalize (GObject * object);
++static guintptr gst_gl_display_gbm_get_handle (GstGLDisplay * display);
++
++
++static void
++gst_gl_display_gbm_class_init (GstGLDisplayGBMClass * klass)
++{
++  g_type_class_add_private (klass, sizeof (GstGLDisplayGBMPrivate));
++
++  GST_GL_DISPLAY_CLASS (klass)->get_handle =
++      GST_DEBUG_FUNCPTR (gst_gl_display_gbm_get_handle);
++
++  G_OBJECT_CLASS (klass)->finalize = gst_gl_display_gbm_finalize;
++}
++
++static void
++gst_gl_display_gbm_init (GstGLDisplayGBM * display_gbm)
++{
++  GstGLDisplay *display = (GstGLDisplay *) display_gbm;
++  display->type = GST_GL_DISPLAY_TYPE_GBM;
++
++  display_gbm->priv = GST_GL_DISPLAY_GBM_GET_PRIVATE (display_gbm);
++  display_gbm->priv->drm_fd = -1;
++}
++
++static void
++gst_gl_display_gbm_finalize (GObject * object)
++{
++  GstGLDisplayGBM *display_gbm = GST_GL_DISPLAY_GBM (object);
++
++  gst_gl_display_gbm_shutdown_gbm (display_gbm->priv);
++  gst_gl_display_gbm_shutdown_drm (display_gbm->priv);
++
++  G_OBJECT_CLASS (gst_gl_display_gbm_parent_class)->finalize (object);
++}
++
++static guintptr
++gst_gl_display_gbm_get_handle (GstGLDisplay * display)
++{
++  return (guintptr) GST_GL_DISPLAY_GBM (display)->priv->gbm_dev;
++}
++
++
++static void
++_init_debug (void)
++{
++  static volatile gsize _init = 0;
++
++  if (g_once_init_enter (&_init)) {
++    GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay");
++    GST_DEBUG_CATEGORY_INIT (gst_gl_gbm_debug, "gleglgbm", 0,
++        "Mesa3D EGL GBM debugging");
++    g_once_init_leave (&_init, 1);
++  }
++}
++
++
++GstGLDisplayGBM *
++gst_gl_display_gbm_new (void)
++{
++  int drm_fd = -1;
++  GstGLDisplayGBM *display;
++  const gchar *drm_node_name;
++
++  _init_debug ();
++
++  drm_node_name = g_getenv ("GST_GL_GBM_DRM_DEVICE");
++
++  if (drm_node_name != NULL) {
++    GST_DEBUG ("attempting to open device %s (specified by the "
++        "GST_GL_GBM_DRM_DEVICE environment variable)", drm_node_name);
++    drm_fd = open (drm_node_name, O_RDWR | O_CLOEXEC);
++    if (drm_fd < 0) {
++      GST_ERROR ("could not open DRM device %s: %s (%d)", drm_node_name,
++          g_strerror (errno), errno);
++      return NULL;
++    }
++  } else {
++    GST_DEBUG ("GST_GL_GBM_DRM_DEVICE environment variable is not "
++        "set - trying to autodetect device");
++    drm_fd = gst_gl_gbm_find_and_open_drm_node ();
++    if (drm_fd < 0) {
++      GST_ERROR ("could not find or open DRM device");
++      return NULL;
++    }
++  }
++
++  display = g_object_new (GST_TYPE_GL_DISPLAY_GBM, NULL);
++  display->priv->drm_fd = drm_fd;
++
++  if (!gst_gl_display_gbm_setup_drm (display->priv)) {
++    GST_ERROR ("Failed to initialize DRM");
++    goto cleanup;
++  }
++
++  if (!gst_gl_display_gbm_setup_gbm (display->priv)) {
++    GST_ERROR ("Failed to initialize GBM");
++    goto cleanup;
++  }
++
++  GST_DEBUG ("Created GBM EGL display %p", (gpointer) display);
++
++  return display;
++
++cleanup:
++  gst_gl_display_gbm_shutdown_gbm (display->priv);
++  gst_gl_display_gbm_shutdown_drm (display->priv);
++  gst_object_unref (G_OBJECT (display));
++  if (drm_fd >= 0)
++    close (drm_fd);
++  return NULL;
++}
+diff --git a/gst-libs/gst/gl/gbm/gstgldisplay_gbm.h b/gst-libs/gst/gl/gbm/gstgldisplay_gbm.h
+new file mode 100644
+index 0000000..ba7f737
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstgldisplay_gbm.h
+@@ -0,0 +1,68 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#ifndef __GST_GL_DISPLAY_GBM_H__
++#define __GST_GL_DISPLAY_GBM_H__
++
++#include <gst/gst.h>
++#include <gst/gl/gstgldisplay.h>
++#include <gst/gl/egl/gstegl.h>
++
++G_BEGIN_DECLS
++
++GType gst_gl_display_gbm_get_type (void);
++
++#define GST_TYPE_GL_DISPLAY_GBM             (gst_gl_display_gbm_get_type())
++#define GST_GL_DISPLAY_GBM(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GL_DISPLAY_GBM,GstGLDisplayGBM))
++#define GST_GL_DISPLAY_GBM_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_GL_DISPLAY_GBM,GstGLDisplayGBMClass))
++#define GST_IS_GL_DISPLAY_GBM(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GL_DISPLAY_GBM))
++#define GST_IS_GL_DISPLAY_GBM_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_GL_DISPLAY_GBM))
++#define GST_GL_DISPLAY_GBM_CAST(obj)        ((GstGLDisplayGBM*)(obj))
++
++#define GST_GL_DISPLAY_GBM_PRIVATE(obj)     (((GstGLDisplayGBM*)(obj))->priv)
++
++typedef struct _GstGLDisplayGBM GstGLDisplayGBM;
++typedef struct _GstGLDisplayGBMClass GstGLDisplayGBMClass;
++typedef struct _GstGLDisplayGBMPrivate GstGLDisplayGBMPrivate;
++
++struct _GstGLDisplayGBM
++{
++  GstGLDisplay parent;
++
++  /* <private> */
++  GstGLDisplayGBMPrivate *priv;
++
++  gpointer _reserved[GST_PADDING];
++};
++
++struct _GstGLDisplayGBMClass
++{
++  GstGLDisplayClass object_class;
++
++  /*< private >*/
++  gpointer _reserved[GST_PADDING_LARGE];
++};
++
++//int gst_gl_display_gbm_find_and_open_drm_node (void);
++GstGLDisplayGBM *gst_gl_display_gbm_new (void);
++
++G_END_DECLS
++
++#endif /* __GST_GL_DISPLAY_GBM_H__ */
+diff --git a/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.c b/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.c
+new file mode 100644
+index 0000000..f9c7115
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.c
+@@ -0,0 +1,367 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include <poll.h>
++
++#include "../gstgl_fwd.h"
++#include <gst/gl/gstglcontext.h>
++#include <gst/gl/egl/gstglcontext_egl.h>
++
++#include "gstgldisplay_gbm.h"
++#include "gstglwindow_gbm_egl.h"
++#include "gstgl_gbm_private.h"
++
++#define GST_CAT_DEFAULT gst_gl_window_debug
++
++
++#define GST_GL_WINDOW_GBM_EGL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
++    GST_TYPE_GL_WINDOW_GBM_EGL, GstGLWindowGBMEGLPrivate))
++
++
++G_DEFINE_TYPE (GstGLWindowGBMEGL, gst_gl_window_gbm_egl, GST_GL_TYPE_WINDOW);
++
++
++static guintptr gst_gl_window_gbm_egl_get_window_handle (GstGLWindow * window);
++static guintptr gst_gl_window_gbm_egl_get_display (GstGLWindow * window);
++static void gst_gl_window_gbm_egl_set_window_handle (GstGLWindow * window,
++    guintptr handle);
++static void gst_gl_window_gbm_egl_close (GstGLWindow * window);
++static void gst_gl_window_gbm_egl_draw (GstGLWindow * window);
++
++static gboolean gst_gl_window_gbm_init_surface (GstGLWindowGBMEGL * window_egl);
++static gboolean
++gst_gl_window_gbm_setup_framebuffer_output (GstGLWindowGBMEGL * window_egl);
++static void gst_gl_window_gbm_egl_cleanup (GstGLWindowGBMEGL * window_egl);
++
++
++
++static void
++gst_gl_window_gbm_egl_class_init (GstGLWindowGBMEGLClass * klass)
++{
++  GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
++
++  g_type_class_add_private (klass, sizeof (GstGLWindowGBMEGLPrivate));
++
++  window_class->get_window_handle =
++      GST_DEBUG_FUNCPTR (gst_gl_window_gbm_egl_get_window_handle);
++  window_class->get_display =
++      GST_DEBUG_FUNCPTR (gst_gl_window_gbm_egl_get_display);
++  window_class->set_window_handle =
++      GST_DEBUG_FUNCPTR (gst_gl_window_gbm_egl_set_window_handle);
++  window_class->close = GST_DEBUG_FUNCPTR (gst_gl_window_gbm_egl_close);
++  window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_gbm_egl_draw);
++
++  // TODO: add support for set_render_rectangle (assuming this functionality
++  // is possible with libdrm/gbm)
++}
++
++
++static void
++gst_gl_window_gbm_egl_init (GstGLWindowGBMEGL * window_gbm)
++{
++  window_gbm->priv = GST_GL_WINDOW_GBM_EGL_GET_PRIVATE (window_gbm);
++  window_gbm->priv->gbm_surf = NULL;
++  window_gbm->priv->current_bo = NULL;
++}
++
++
++static guintptr
++gst_gl_window_gbm_egl_get_window_handle (GstGLWindow * window)
++{
++  GstGLWindowGBMEGL *window_egl = GST_GL_WINDOW_GBM_EGL (window);
++
++  /* This function is called in here, and not in the open()
++   * vmethod. The reason for this is explained inside the
++   * gst_gl_window_gbm_init_surface() function. */
++  if (window_egl->priv->gbm_surf == NULL) {
++    if (!gst_gl_window_gbm_init_surface (window_egl))
++      return 0;
++  }
++
++  return (guintptr) GST_GL_WINDOW_GBM_EGL (window)->priv->gbm_surf;
++}
++
++
++static guintptr
++gst_gl_window_gbm_egl_get_display (GstGLWindow * window)
++{
++  return gst_gl_display_get_handle (window->display);
++}
++
++
++static void
++gst_gl_window_gbm_egl_set_window_handle (G_GNUC_UNUSED GstGLWindow * window,
++    G_GNUC_UNUSED guintptr handle)
++{
++  // TODO: Currently, it is unclear how to use external GBM buffer objects,
++  // since it is not defined how this would work together with DRM page flips
++}
++
++
++static void
++gst_gl_window_gbm_egl_close (GstGLWindow * window)
++{
++  GstGLWindowGBMEGL *window_egl = GST_GL_WINDOW_GBM_EGL (window);
++
++  gst_gl_window_gbm_egl_cleanup (window_egl);
++
++  GST_GL_WINDOW_CLASS (gst_gl_window_gbm_egl_parent_class)->close (window);
++}
++
++
++static void
++_page_flip_handler (G_GNUC_UNUSED int fd, G_GNUC_UNUSED unsigned int frame,
++    G_GNUC_UNUSED unsigned int sec, G_GNUC_UNUSED unsigned int usec, void *data)
++{
++  /* If we reach this point, it means the page flip has been completed.
++   * Signal this by clearing the flag so the poll() loop in draw_cb()
++   * can exit. */
++  int *waiting_for_flip = data;
++  *waiting_for_flip = 0;
++}
++
++static void
++draw_cb (gpointer data)
++{
++  GstGLWindowGBMEGL *window_egl = data;
++  GstGLWindow *window = GST_GL_WINDOW (window_egl);
++  GstGLContext *context = gst_gl_window_get_context (window);
++  GstGLContextClass *context_class = GST_GL_CONTEXT_GET_CLASS (context);
++  GstGLDisplayGBMPrivate *disp_priv = window_egl->priv->display_priv;
++  struct gbm_bo *next_bo;
++  GstGLDRMFramebuffer *framebuf;
++  int waiting_for_flip;
++  int ret;
++
++  drmEventContext evctx = {
++    .version = DRM_EVENT_CONTEXT_VERSION,
++    .page_flip_handler = _page_flip_handler,
++  };
++
++  struct pollfd pfd = {
++    .fd = disp_priv->drm_fd,
++    .events = POLLIN,
++    .revents = 0,
++  };
++
++  /* Framebuffer output is set up here, and not in the open() vmethod
++   * or in gst_gl_window_gbm_init_surface(). The reason for this is that
++   * eglInitialize() loads the Mesa DRI2 driver, and open() vmethods are
++   * called *before* eglInitialize(). Furthermore, GBM front buffer
++   * functions need an initialized DRI2 surface, which is set up by the
++   * eglCreateWindowSurface() function (and this one is called *before*
++   * the get_window_handle() vmethod). */
++  if (window_egl->priv->current_bo == NULL) {
++    if (!gst_gl_window_gbm_setup_framebuffer_output (window_egl)) {
++      GST_ERROR ("Could not set up page flipping");
++      /* XXX: it is not possible to communicate the error to the pipeline */
++      return;
++    }
++  }
++
++  /* Do the actual drawing */
++  if (window->draw)
++    window->draw (window->draw_data);
++
++  /* Let the context class call eglSwapBuffers() */
++  context_class->swap_buffers (context);
++  gst_object_unref (context);
++
++  /* eglSwapBuffers() does not do any page flipping. We have to do
++   * this ourselves. We retrieve a buffer object (in short "bo") for the new
++   * front buffer so we can retrieve a framebuffer ID out of it. */
++  next_bo = gbm_surface_lock_front_buffer (window_egl->priv->gbm_surf);
++  framebuf = gst_gl_gbm_drm_fb_get_from_bo (next_bo);
++
++  /* Schedule a page-flip to the new front buffer by calling drmModePageFlip().
++   * This call is similar to calling drmModeSetCrtc() with next_bo's framebuffer
++   * ID, except that drmModePageFlip() waits for VBlank until pages can be flipped,
++   * so we can avoid tearing by using it instead.
++   *
++   * The page flip happens asynchronously, so we wait for it using a flag
++   * (waiting_for_flip) which is set to 0 in the _page_flip_handler() callback.
++   * See the poll() loop below for more. */
++  waiting_for_flip = 1;
++  ret = drmModePageFlip (disp_priv->drm_fd, disp_priv->crtc_id, framebuf->fb_id,
++      DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
++  if (ret != 0) {
++    /* NOTE: According to libdrm sources, the page is _not_
++     * considered flipped if drmModePageFlip() reports an error,
++     * so we do not update the priv->current_bo pointer here */
++    GST_ERROR ("Could not initialize GBM surface");
++    /* XXX: it is not possible to communicate the error to the pipeline */
++    return;
++  }
++
++  /* As mentioned above, the page flip happens asynchronously. The page flip
++   * event is signaled through DRM file descriptor, so by using poll() we
++   * wait until there is something to read from the FD. drmHandleEvent() then
++   * takes care of reading whatever data there is, and if it got informed
++   * that the page flip ended, _page_flip_handler() is called. */
++  while (waiting_for_flip) {
++    ret = poll (&pfd, 1, -1);
++    if (ret < 0) {
++      if (errno == EINTR)
++        GST_DEBUG ("Signal caught during poll() call");
++      else
++        GST_ERROR ("poll() failed: %s (%d)", g_strerror (errno), errno);
++      /* XXX: it is not possible to communicate errors and interruptions
++       * to the pipeline */
++      return;
++    }
++
++    drmHandleEvent (disp_priv->drm_fd, &evctx);
++  }
++
++  /* The buffer object associated with the former front buffer is
++   * returned here to the surface. Note that this does _not_ deallocate
++   * any framebuffer, it just returns it to the surface structure and
++   * is reused the next time buffers are swapped by eglSwapBuffers(). */
++  gbm_surface_release_buffer (window_egl->priv->gbm_surf,
++      window_egl->priv->current_bo);
++  window_egl->priv->current_bo = next_bo;
++}
++
++
++static void
++gst_gl_window_gbm_egl_draw (GstGLWindow * window)
++{
++  gst_gl_window_send_message (window, (GstGLWindowCB) draw_cb, window);
++}
++
++
++static gboolean
++gst_gl_window_gbm_init_surface (GstGLWindowGBMEGL * window_egl)
++{
++  /* NOTE: This function cannot be called in the open() vmethod
++   * since context_egl->egl_display and context_egl->egl_config
++   * must have been set to valid values at this point, and open()
++   * is called _before_ these are set.
++   * Also, eglInitialize() is called _after_ the open() vmethod,
++   * which means that the return value of gbm_surface_create()
++   * contains some function pointers that are set to NULL and
++   * shouldn't be. This is because Mesa's eglInitialize() loads
++   * the DRI2 driver and the relevant functions aren't available
++   * until then. */
++
++  GstGLWindow *window = GST_GL_WINDOW (window_egl);
++  GstGLDisplayGBMPrivate *disp_priv = window_egl->priv->display_priv;
++  drmModeModeInfo *drm_mode_info = disp_priv->drm_mode_info;
++  GstGLContext *context = gst_gl_window_get_context (window);
++  GstGLContextEGL *context_egl = GST_GL_CONTEXT_EGL (context);
++  EGLint gbm_format;
++
++  /* With GBM-based EGL displays and configs, the native visual ID
++   * is a GBM pixel format */
++  if (!eglGetConfigAttrib (context_egl->egl_display, context_egl->egl_config,
++          EGL_NATIVE_VISUAL_ID, &gbm_format)) {
++    GST_ERROR ("eglGetConfigAttrib failed: %s",
++        gst_egl_get_error_string (eglGetError ()));
++    return FALSE;
++  }
++
++  /* Create a GBM surface that we'll use for rendering into. GBM
++   * surfaces can also contain multiple framebuffers, which is
++   * a way to implement double buffering. */
++  window_egl->priv->gbm_surf = gbm_surface_create (disp_priv->gbm_dev,
++      drm_mode_info->hdisplay, drm_mode_info->vdisplay, gbm_format,
++      GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
++
++  gst_gl_window_resize (window, drm_mode_info->hdisplay,
++      drm_mode_info->vdisplay);
++
++  GST_DEBUG ("Successfully created GBM surface");
++
++  return TRUE;
++}
++
++
++static gboolean
++gst_gl_window_gbm_setup_framebuffer_output (GstGLWindowGBMEGL * window_egl)
++{
++  GstGLWindow *window = GST_GL_WINDOW (window_egl);
++  GstGLDisplayGBMPrivate *disp_priv = window_egl->priv->display_priv;
++  GstGLContext *context = gst_gl_window_get_context (window);
++  GstGLContextClass *context_class;
++  GstGLDRMFramebuffer *framebuf;
++  int ret;
++
++  context_class = GST_GL_CONTEXT_GET_CLASS (context);
++  context_class->swap_buffers (context);
++  gst_object_unref (context);
++
++  /* Retrieve the buffer object of the current front buffer and set it as
++   * the current_bo. Later in draw_cb() similar calls are done to retrieve
++   * buffer objects for the new front buffer, after eglSwapBuffers() swapped
++   * front- and backbuffers. */
++  window_egl->priv->current_bo =
++      gbm_surface_lock_front_buffer (window_egl->priv->gbm_surf);
++  framebuf = gst_gl_gbm_drm_fb_get_from_bo (window_egl->priv->current_bo);
++
++  /* Setup CRTC configuration, including the source framebuffer, the x/y
++   * positions (set to 0,0), the connector ID etc. Later on, when the pages
++   * are flipped, several of these values are reused by drmModePageFlip().
++   * (The framebuffer ID is a different one of course.) */
++  ret = drmModeSetCrtc (disp_priv->drm_fd, disp_priv->crtc_id, framebuf->fb_id,
++      0, 0, &(disp_priv->drm_mode_connector->connector_id), 1,
++      disp_priv->drm_mode_info);
++
++  if (ret != 0) {
++    GST_ERROR ("Could not set DRM CRTC: %s (%d)", g_strerror (errno), errno);
++    return FALSE;
++  }
++
++  GST_DEBUG ("DRM CRTC set");
++
++  return TRUE;
++}
++
++
++static void
++gst_gl_window_gbm_egl_cleanup (GstGLWindowGBMEGL * window_egl)
++{
++  if (window_egl->priv->gbm_surf != NULL) {
++    if (window_egl->priv->current_bo != NULL) {
++      gbm_surface_release_buffer (window_egl->priv->gbm_surf,
++          window_egl->priv->current_bo);
++      window_egl->priv->current_bo = NULL;
++    }
++
++    gbm_surface_destroy (window_egl->priv->gbm_surf);
++    window_egl->priv->gbm_surf = NULL;
++  }
++}
++
++
++/* Must be called in the gl thread */
++GstGLWindowGBMEGL *
++gst_gl_window_gbm_egl_new (GstGLDisplay * display)
++{
++  GstGLWindowGBMEGL *window_egl;
++
++  if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_GBM) == 0)
++    /* we require a GBM display to create windows */
++    return NULL;
++
++  window_egl = g_object_new (GST_TYPE_GL_WINDOW_GBM_EGL, NULL);
++  window_egl->priv->display_priv = GST_GL_DISPLAY_GBM_PRIVATE (display);
++
++  return window_egl;
++}
+diff --git a/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.h b/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.h
+new file mode 100644
+index 0000000..5735b0a
+--- /dev/null
++++ b/gst-libs/gst/gl/gbm/gstglwindow_gbm_egl.h
+@@ -0,0 +1,64 @@
++/*
++ * GStreamer
++ * Copyright (C) 2017 Carlos Rafael Giani <dv@pseudoterminal.org>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#ifndef __GST_GL_WINDOW_GBM_EGL_H__
++#define __GST_GL_WINDOW_GBM_EGL_H__
++
++#include <gst/gl/gl.h>
++#include <gst/gl/egl/gstegl.h>
++
++G_BEGIN_DECLS
++
++#define GST_TYPE_GL_WINDOW_GBM_EGL         (gst_gl_window_gbm_egl_get_type())
++#define GST_GL_WINDOW_GBM_EGL(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_GL_WINDOW_GBM_EGL, GstGLWindowGBMEGL))
++#define GST_GL_WINDOW_GBM_EGL_CLASS(k)     (G_TYPE_CHECK_CLASS((k), GST_TYPE_GL_WINDOW_GBM_EGL, GstGLWindowGBMEGLClass))
++#define GST_IS_GL_WINDOW_GBM_EGL(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_GL_WINDOW_GBM_EGL))
++#define GST_IS_GL_WINDOW_GBM_EGL_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_GL_WINDOW_GBM_EGL))
++#define GST_GL_WINDOW_GBM_EGL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_GL_WINDOW_GBM_EGL, GstGLWindowGBMEGL_Class))
++
++typedef struct _GstGLWindowGBMEGL        GstGLWindowGBMEGL;
++typedef struct _GstGLWindowGBMEGLClass   GstGLWindowGBMEGLClass;
++typedef struct _GstGLWindowGBMEGLPrivate GstGLWindowGBMEGLPrivate;
++
++struct _GstGLWindowGBMEGL {
++  /*< private >*/
++  GstGLWindow parent;
++
++  /* <private> */
++  GstGLWindowGBMEGLPrivate *priv;
++
++  gpointer _reserved[GST_PADDING];
++};
++
++struct _GstGLWindowGBMEGLClass {
++  /*< private >*/
++  GstGLWindowClass parent_class;
++
++  /*< private >*/
++  gpointer _reserved[GST_PADDING_LARGE];
++};
++
++GType gst_gl_window_gbm_egl_get_type (void);
++
++GstGLWindowGBMEGL * gst_gl_window_gbm_egl_new (GstGLDisplay * display);
++
++G_END_DECLS
++
++#endif /* __GST_GL_WINDOW_GBM_EGL_H__ */
+diff --git a/gst-libs/gst/gl/gstgldisplay.c b/gst-libs/gst/gl/gstgldisplay.c
+index 6408f0c..90e02b9 100644
+--- a/gst-libs/gst/gl/gstgldisplay.c
++++ b/gst-libs/gst/gl/gstgldisplay.c
+@@ -73,6 +73,9 @@
+ #if GST_GL_HAVE_WINDOW_VIV_FB
+ #include <gst/gl/viv-fb/gstgldisplay_viv_fb.h>
+ #endif
++#if GST_GL_HAVE_WINDOW_GBM
++#include <gst/gl/gbm/gstgldisplay_gbm.h>
++#endif
+ GST_DEBUG_CATEGORY_STATIC (gst_context);
+ GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
+@@ -313,6 +316,11 @@ gst_gl_display_new (void)
+     display = GST_GL_DISPLAY (gst_gl_display_viv_fb_new (disp_idx));
+   }
+ #endif
++#if GST_GL_HAVE_WINDOW_GBM
++  if (!display && (!user_choice || g_strstr_len (user_choice, 3, "gbm"))) {
++    display = GST_GL_DISPLAY (gst_gl_display_gbm_new ());
++  }
++#endif
+ #if GST_GL_HAVE_PLATFORM_EGL
+   if (!display && (!platform_choice
+           || g_strstr_len (platform_choice, 3, "egl")))
+diff --git a/gst-libs/gst/gl/gstgldisplay.h b/gst-libs/gst/gl/gstgldisplay.h
+index deac9df..35b1806 100644
+--- a/gst-libs/gst/gl/gstgldisplay.h
++++ b/gst-libs/gst/gl/gstgldisplay.h
+@@ -50,6 +50,7 @@ GType gst_gl_display_get_type (void);
+  * @GST_GL_DISPLAY_TYPE_WIN32: Win32 display
+  * @GST_GL_DISPLAY_TYPE_DISPMANX: Dispmanx display
+  * @GST_GL_DISPLAY_TYPE_EGL: EGL display
++ * @GST_GL_DISPLAY_TYPE_GBM: Mesa3D GBM display
+  * @GST_GL_DISPLAY_TYPE_ANY: any display type
+  */
+ typedef enum
+@@ -62,6 +63,7 @@ typedef enum
+   GST_GL_DISPLAY_TYPE_DISPMANX = (1 << 4),
+   GST_GL_DISPLAY_TYPE_EGL = (1 << 5),
+   GST_GL_DISPLAY_TYPE_VIV_FB = (1 << 6),
++  GST_GL_DISPLAY_TYPE_GBM = (1 << 7),
+   GST_GL_DISPLAY_TYPE_ANY = G_MAXUINT32
+ } GstGLDisplayType;
+diff --git a/gst-libs/gst/gl/gstglwindow.c b/gst-libs/gst/gl/gstglwindow.c
+index d279b22..5494c70 100644
+--- a/gst-libs/gst/gl/gstglwindow.c
++++ b/gst-libs/gst/gl/gstglwindow.c
+@@ -61,6 +61,9 @@
+ #if GST_GL_HAVE_WINDOW_VIV_FB
+ #include "viv-fb/gstglwindow_viv_fb_egl.h"
+ #endif
++#if GST_GL_HAVE_WINDOW_GBM
++#include "gbm/gstglwindow_gbm_egl.h"
++#endif
+ #if GST_GL_HAVE_WINDOW_DISPMANX
+ #include "dispmanx/gstglwindow_dispmanx_egl.h"
+ #endif
+@@ -278,6 +281,10 @@ gst_gl_window_new (GstGLDisplay * display)
+   if (!window && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb")))
+     window = GST_GL_WINDOW (gst_gl_window_viv_fb_egl_new (display));
+ #endif
++#if GST_GL_HAVE_WINDOW_GBM
++  if (!window && (!user_choice || g_strstr_len (user_choice, 3, "gbm")))
++    window = GST_GL_WINDOW (gst_gl_window_gbm_egl_new (display));
++#endif
+   if (!window) {
+     /* subclass returned a NULL window */
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.12.%.bbappend b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.12.%.bbappend
new file mode 100644 (file)
index 0000000..5890c53
--- /dev/null
@@ -0,0 +1,14 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+PACKAGECONFIG_GL_mainline-bsp = "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2', '', d)}"
+
+SRC_URI_append = " \
+    file://mesa3d-gbm/0001-qmlglsink-Add-dummy-texture-that-is-shown-as-placeho.patch \
+    file://mesa3d-gbm/0002-gl-viv-fb-Fix-user-choice-string-comparisons.patch \
+    file://mesa3d-gbm/0003-configure-Factor-out-libdrm-to-make-the-configuratio.patch \
+    file://mesa3d-gbm/0004-configure-Factor-out-gudev-checks.patch \
+    file://mesa3d-gbm/0005-gl-Add-Mesa3D-GBM-backend.patch \
+    file://direct-dmabuf-uploader/0001-gstgl-Fix-sanity-check-in-gst_gl_memory_setup_buffer.patch \
+    file://direct-dmabuf-uploader/0002-gl-egl-Add-gst_egl_image_from_dmabuf_direct-function.patch \
+    file://direct-dmabuf-uploader/0003-gstgl-Add-direct-DMABUF-uploader-WIP.patch \
+"
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good/0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good/0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch
new file mode 100644 (file)
index 0000000..f8a5e30
--- /dev/null
@@ -0,0 +1,41 @@
+From faec2f8b60cb8b01dacb7e3dd22367dda3ea5d94 Mon Sep 17 00:00:00 2001
+From: Carlos Rafael Giani <dv@pseudoterminal.org>
+Date: Wed, 7 Jun 2017 20:15:24 +0200
+Subject: [PATCH] v4l2videodec: Hack to make sure DMABUF is the default capture
+ io mode
+
+This is necessary to guarantee that zerocopy is used for video decoding.
+Zerocopy requires dmabuf usage. And without zerocopy, video playback is
+slow because pixels get copied by the CPU.
+
+Upstream is already working on a better solution that sets dmabuf as
+default: https://bugzilla.gnome.org/show_bug.cgi?id=779466
+
+Upstream-Status: Inapproproate [i.MX/CODA specific hack]
+---
+ sys/v4l2/gstv4l2videodec.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/sys/v4l2/gstv4l2videodec.c b/sys/v4l2/gstv4l2videodec.c
+index 2c07c30..4c7b1e6 100644
+--- a/sys/v4l2/gstv4l2videodec.c
++++ b/sys/v4l2/gstv4l2videodec.c
+@@ -898,6 +898,15 @@ gst_v4l2_video_dec_subinstance_init (GTypeInstance * instance, gpointer g_class)
+       gst_v4l2_get_input, gst_v4l2_set_input, NULL);
+   self->v4l2capture->no_initial_format = TRUE;
+   self->v4l2output->keep_aspect = FALSE;
++
++  /* XXX Hack to make sure dmabuf is used as default for capture io mode */
++  {
++    GValue val = G_VALUE_INIT;
++    g_value_init (&val, GST_TYPE_V4L2_IO_MODE);
++    g_value_set_enum (&val, GST_V4L2_IO_DMABUF);
++    gst_v4l2_object_set_property_helper (self->v4l2capture,
++        PROP_CAPTURE_IO_MODE, &val, NULL);
++  }
+ }
+ static void
+-- 
+2.7.4
+
diff --git a/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.12.%.bbappend b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.12.%.bbappend
new file mode 100644 (file)
index 0000000..9db3236
--- /dev/null
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append = " \
+    file://0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch \
+"