From: Carlos Rafael Giani Date: Mon, 12 Jun 2017 22:27:20 +0000 (+0200) Subject: gstreamer: Add new uploader and GBM GL backend for video playback via GL X-Git-Url: https://code.ossystems.io/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F28%2F15828%2F1;p=meta-freescale.git gstreamer: Add new uploader and GBM GL backend for video playback via GL Change-Id: I711bf82193279070d922ce6e2427268ce8989aac Signed-off-by: Carlos Rafael Giani --- 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 index 00000000..fce6d3b4 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0001-gstgl-Fix-sanity-check-in-gst_gl_memory_setup_buffer.patch @@ -0,0 +1,28 @@ +From f8ff1002df589853edce4aee2a2d3714a6f2971f Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 index 00000000..14c5b56f --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0002-gl-egl-Add-gst_egl_image_from_dmabuf_direct-function.patch @@ -0,0 +1,341 @@ +From 3232363bfb6a062023514416883a7ed50802a48b Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 +
+ gsteglimage + GstEGLImage +-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 index 00000000..a2e7e546 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/direct-dmabuf-uploader/0003-gstgl-Add-direct-DMABUF-uploader-WIP.patch @@ -0,0 +1,378 @@ +From 2d01625e1cf1100ed0c8b01ce8d04e56fd3198ef Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 ++#include + #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 index 00000000..adc8dc56 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0001-qmlglsink-Add-dummy-texture-that-is-shown-as-placeho.patch @@ -0,0 +1,107 @@ +From bb3edd3cc00068ae8f824e52aa656744e95be99c Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 + #include + + #include +@@ -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 index 00000000..4cbf8d97 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0002-gl-viv-fb-Fix-user-choice-string-comparisons.patch @@ -0,0 +1,40 @@ +From a1d44ee3fbc517b6515f41e98871b01a7608623c Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 index 00000000..0a15dce3 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0003-configure-Factor-out-libdrm-to-make-the-configuratio.patch @@ -0,0 +1,124 @@ +From 9e42946256f4cdc09b2c43d5779dbbfba464f684 Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 index 00000000..75fd05e4 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0004-configure-Factor-out-gudev-checks.patch @@ -0,0 +1,41 @@ +From c3d798ee260eb3e8fbef9066c880bed3e86e633b Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 index 00000000..f045051f --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/mesa3d-gbm/0005-gl-Add-Mesa3D-GBM-backend.patch @@ -0,0 +1,1833 @@ +From 246b92374f34472c1a936d9a1fb8ce67e596eef5 Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 ++ * ++ * 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 ++#include ++#include ++#include ++#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 ""; ++ } ++} ++ ++ ++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 ""; ++ } ++} ++ ++ ++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 ""; ++ } ++ ++ 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++ ++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 ++ * ++ * 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 ++#include ++#include ++ ++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 ++ * ++ * 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 ++#include ++#include ++ ++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; ++ ++ /* */ ++ 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 ++ * ++ * 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 ++ ++#include "../gstgl_fwd.h" ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++ ++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; ++ ++ /* */ ++ 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 + #endif ++#if GST_GL_HAVE_WINDOW_GBM ++#include ++#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 index 00000000..5890c53a --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.12.%.bbappend @@ -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 index 00000000..f8a5e300 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good/0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch @@ -0,0 +1,41 @@ +From faec2f8b60cb8b01dacb7e3dd22367dda3ea5d94 Mon Sep 17 00:00:00 2001 +From: Carlos Rafael Giani +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 index 00000000..9db32363 --- /dev/null +++ b/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.12.%.bbappend @@ -0,0 +1,5 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" + +SRC_URI_append = " \ + file://0001-v4l2videodec-Hack-to-make-sure-DMABUF-is-the-default.patch \ +"