added my Recipes

This commit is contained in:
2024-07-11 14:16:35 +02:00
parent 38bc4f53ac
commit 09b621d929
7118 changed files with 525762 additions and 3 deletions

View File

@@ -0,0 +1,489 @@
From bc7c1d8febed84d33f0e49272b7707059485d4b4 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Tue, 28 Mar 2023 18:24:40 +0200
Subject: [PATCH 01/17] kmsallocator: Port to the new DRM Dumb Allocator
This ports the KMS allocator to use the DRM Dumb allocator from the allocators
library.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
sys/kms/gstkmsallocator.c | 237 +++++++++++++-------------------------
sys/kms/gstkmsallocator.h | 5 +-
2 files changed, 84 insertions(+), 158 deletions(-)
diff --git a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c
index 6687f3b..3d9ee0a 100644
--- a/sys/kms/gstkmsallocator.c
+++ b/sys/kms/gstkmsallocator.c
@@ -38,6 +38,7 @@
#include <drm.h>
#include <gst/allocators/gstdmabuf.h>
+#include <gst/allocators/gstdrmdumb.h>
#include "gstkmsallocator.h"
#include "gstkmsutils.h"
@@ -51,20 +52,12 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_KMS_MEMORY_TYPE "KMSMemory"
-struct kms_bo
-{
- void *ptr;
- size_t size;
- unsigned handle;
- unsigned int refs;
-};
-
struct _GstKMSAllocatorPrivate
{
int fd;
/* protected by GstKMSAllocator object lock */
GList *mem_cache;
- GstAllocator *dmabuf_alloc;
+ GstAllocator *dumb_alloc;
};
#define parent_class gst_kms_allocator_parent_class
@@ -101,92 +94,40 @@ check_fd (GstKMSAllocator * alloc)
return alloc->priv->fd > -1;
}
-static void
-gst_kms_allocator_memory_reset (GstKMSAllocator * allocator, GstKMSMemory * mem)
-{
- int err;
- struct drm_mode_destroy_dumb arg = { 0, };
-
- if (!check_fd (allocator))
- return;
-
- if (mem->fb_id) {
- GST_DEBUG_OBJECT (allocator, "removing fb id %d", mem->fb_id);
- drmModeRmFB (allocator->priv->fd, mem->fb_id);
- mem->fb_id = 0;
- }
-
- if (!mem->bo)
- return;
-
- if (mem->bo->ptr != NULL) {
- GST_WARNING_OBJECT (allocator, "destroying mapped bo (refcount=%d)",
- mem->bo->refs);
- munmap (mem->bo->ptr, mem->bo->size);
- mem->bo->ptr = NULL;
- }
-
- arg.handle = mem->bo->handle;
-
- err = drmIoctl (allocator->priv->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
- if (err)
- GST_WARNING_OBJECT (allocator,
- "Failed to destroy dumb buffer object: %s %d",
- g_strerror (errno), errno);
-
- g_free (mem->bo);
- mem->bo = NULL;
-}
-
static gboolean
gst_kms_allocator_memory_create (GstKMSAllocator * allocator,
GstKMSMemory * kmsmem, GstVideoInfo * vinfo)
{
- gint i, ret, h;
- struct drm_mode_create_dumb arg = { 0, };
- guint32 fmt;
+ gint i, h;
gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo);
gsize offs = 0;
+ guint32 pitch;
if (kmsmem->bo)
return TRUE;
- if (!check_fd (allocator))
- return FALSE;
-
- kmsmem->bo = g_malloc0 (sizeof (*kmsmem->bo));
+ kmsmem->bo = gst_drm_dumb_allocator_alloc (allocator->priv->dumb_alloc,
+ gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo)),
+ GST_VIDEO_INFO_WIDTH (vinfo), GST_VIDEO_INFO_HEIGHT (vinfo), &pitch);
if (!kmsmem->bo)
- return FALSE;
-
- fmt = gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo));
- arg.bpp = gst_drm_bpp_from_drm (fmt);
- arg.width = GST_VIDEO_INFO_WIDTH (vinfo);
- h = GST_VIDEO_INFO_HEIGHT (vinfo);
- arg.height = gst_drm_height_from_drm (fmt, h);
-
- ret = drmIoctl (allocator->priv->fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
- if (ret)
goto create_failed;
- if (!arg.pitch)
+ if (!pitch)
goto done;
+ h = GST_VIDEO_INFO_HEIGHT (vinfo);
for (i = 0; i < num_planes; i++) {
- guint32 pitch;
-
- if (!arg.pitch)
- continue;
+ guint32 stride;
/* Overwrite the video info's stride and offset using the pitch calculcated
* by the kms driver. */
- pitch = gst_video_format_info_extrapolate_stride (vinfo->finfo, i,
- arg.pitch);
+ stride = gst_video_format_info_extrapolate_stride (vinfo->finfo, i, pitch);
GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = pitch;
GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = offs;
/* Note that we cannot negotiate special padding betweem each planes,
* hence using the display height here. */
- offs += pitch * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, h);
+ offs += stride * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, h);
GST_DEBUG_OBJECT (allocator, "Created BO plane %i with stride %i and "
"offset %" G_GSIZE_FORMAT, i,
@@ -198,10 +139,6 @@ gst_kms_allocator_memory_create (GstKMSAllocator * allocator,
GST_VIDEO_INFO_SIZE (vinfo) = offs;
done:
- kmsmem->bo->handle = arg.handle;
- /* will be used a memory maxsize */
- kmsmem->bo->size = arg.size;
-
/* Validate the size to prevent overflow */
if (kmsmem->bo->size < GST_VIDEO_INFO_SIZE (vinfo)) {
GST_ERROR_OBJECT (allocator,
@@ -218,8 +155,6 @@ create_failed:
{
GST_ERROR_OBJECT (allocator, "Failed to create buffer object: %s (%d)",
g_strerror (errno), errno);
- g_free (kmsmem->bo);
- kmsmem->bo = NULL;
return FALSE;
}
}
@@ -233,7 +168,16 @@ gst_kms_allocator_free (GstAllocator * allocator, GstMemory * mem)
alloc = GST_KMS_ALLOCATOR (allocator);
kmsmem = (GstKMSMemory *) mem;
- gst_kms_allocator_memory_reset (alloc, kmsmem);
+ if (check_fd (alloc) && kmsmem->fb_id) {
+ GST_DEBUG_OBJECT (allocator, "removing fb id %d", kmsmem->fb_id);
+ drmModeRmFB (alloc->priv->fd, kmsmem->fb_id);
+ kmsmem->fb_id = 0;
+ }
+
+ if (kmsmem->bo) {
+ gst_memory_unref (kmsmem->bo);
+ kmsmem->bo = NULL;
+ }
g_slice_free (GstKMSMemory, kmsmem);
}
@@ -276,6 +220,22 @@ gst_kms_allocator_get_property (GObject * object, guint prop_id,
}
}
+static void
+gst_kms_allocator_constructed (GObject * obj)
+{
+ GstKMSAllocator *alloc;
+
+ alloc = GST_KMS_ALLOCATOR (obj);
+
+ /* Should be called after the properties are set */
+ g_assert (check_fd (alloc));
+ alloc->priv->dumb_alloc =
+ gst_drm_dumb_allocator_new_with_fd (alloc->priv->fd);
+
+ /* Its already opened and we already checked for dumb allocation support */
+ g_assert (alloc->priv->dumb_alloc);
+}
+
static void
gst_kms_allocator_finalize (GObject * obj)
{
@@ -285,8 +245,8 @@ gst_kms_allocator_finalize (GObject * obj)
gst_kms_allocator_clear_cache (GST_ALLOCATOR (alloc));
- if (alloc->priv->dmabuf_alloc)
- gst_object_unref (alloc->priv->dmabuf_alloc);
+ if (alloc->priv->dumb_alloc)
+ gst_object_unref (alloc->priv->dumb_alloc);
if (check_fd (alloc))
close (alloc->priv->fd);
@@ -307,11 +267,12 @@ gst_kms_allocator_class_init (GstKMSAllocatorClass * klass)
gobject_class->set_property = gst_kms_allocator_set_property;
gobject_class->get_property = gst_kms_allocator_get_property;
+ gobject_class->constructed = gst_kms_allocator_constructed;
gobject_class->finalize = gst_kms_allocator_finalize;
g_props[PROP_DRM_FD] = g_param_spec_int ("drm-fd", "DRM fd",
"DRM file descriptor", -1, G_MAXINT, -1,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, PROP_N, g_props);
}
@@ -320,46 +281,20 @@ static gpointer
gst_kms_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
{
GstKMSMemory *kmsmem;
- GstKMSAllocator *alloc;
- int err;
- gpointer out;
- struct drm_mode_map_dumb arg = { 0, };
-
- alloc = (GstKMSAllocator *) mem->allocator;
-
- if (!check_fd (alloc))
- return NULL;
kmsmem = (GstKMSMemory *) mem;
if (!kmsmem->bo)
return NULL;
- /* Reuse existing buffer object mapping if possible */
- if (kmsmem->bo->ptr != NULL) {
+ if (kmsmem->bo_map.data)
goto out;
- }
- arg.handle = kmsmem->bo->handle;
-
- err = drmIoctl (alloc->priv->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
- if (err) {
- GST_ERROR_OBJECT (alloc, "Failed to get offset of buffer object: %s %d",
- g_strerror (errno), errno);
- return NULL;
- }
-
- out = mmap (0, kmsmem->bo->size,
- PROT_READ | PROT_WRITE, MAP_SHARED, alloc->priv->fd, arg.offset);
- if (out == MAP_FAILED) {
- GST_ERROR_OBJECT (alloc, "Failed to map dumb buffer object: %s %d",
- g_strerror (errno), errno);
+ if (!gst_memory_map (kmsmem->bo, &kmsmem->bo_map, flags))
return NULL;
- }
- kmsmem->bo->ptr = out;
out:
- g_atomic_int_inc (&kmsmem->bo->refs);
- return kmsmem->bo->ptr;
+ g_atomic_int_inc (&kmsmem->bo_map_refs);
+ return kmsmem->bo_map.data;
}
static void
@@ -367,16 +302,13 @@ gst_kms_memory_unmap (GstMemory * mem)
{
GstKMSMemory *kmsmem;
- if (!check_fd ((GstKMSAllocator *) mem->allocator))
- return;
-
kmsmem = (GstKMSMemory *) mem;
if (!kmsmem->bo)
return;
- if (g_atomic_int_dec_and_test (&kmsmem->bo->refs)) {
- munmap (kmsmem->bo->ptr, kmsmem->bo->size);
- kmsmem->bo->ptr = NULL;
+ if (g_atomic_int_dec_and_test (&kmsmem->bo_map_refs)) {
+ gst_memory_unmap (kmsmem->bo, &kmsmem->bo_map);
+ kmsmem->bo_map.data = NULL;
}
}
@@ -414,11 +346,12 @@ gst_kms_allocator_new (int fd)
* which are relative to the GstBuffer start. */
static gboolean
gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
- gsize in_offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo)
+ gsize in_offsets[GST_VIDEO_MAX_PLANES], GstVideoInfo * vinfo,
+ guint32 bo_handles[4])
{
gint i, ret;
gint num_planes = GST_VIDEO_INFO_N_PLANES (vinfo);
- guint32 w, h, fmt, bo_handles[4] = { 0, };
+ guint32 w, h, fmt;
guint32 pitches[4] = { 0, };
guint32 offsets[4] = { 0, };
@@ -430,11 +363,6 @@ gst_kms_allocator_add_fb (GstKMSAllocator * alloc, GstKMSMemory * kmsmem,
fmt = gst_drm_format_from_video (GST_VIDEO_INFO_FORMAT (vinfo));
for (i = 0; i < num_planes; i++) {
- if (kmsmem->bo)
- bo_handles[i] = kmsmem->bo->handle;
- else
- bo_handles[i] = kmsmem->gem_handle[i];
-
pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i);
offsets[i] = in_offsets[i];
}
@@ -459,6 +387,8 @@ gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
GstKMSAllocator *alloc;
GstKMSMemory *kmsmem;
GstMemory *mem;
+ guint32 bo_handle[4] = { 0, };
+ gint i;
kmsmem = g_slice_new0 (GstKMSMemory);
if (!kmsmem)
@@ -474,9 +404,13 @@ gst_kms_allocator_bo_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
}
gst_memory_init (mem, GST_MEMORY_FLAG_NO_SHARE, allocator, NULL,
- kmsmem->bo->size, 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
+ kmsmem->bo->maxsize, 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
+
+ for (i = 0; i < GST_VIDEO_INFO_N_PLANES (vinfo); i++)
+ bo_handle[i] = gst_drm_dumb_memory_get_handle (kmsmem->bo);
- if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo))
+ if (!gst_kms_allocator_add_fb (alloc, kmsmem, vinfo->offset, vinfo,
+ bo_handle))
goto fail;
return mem;
@@ -495,6 +429,7 @@ gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds,
GstKMSMemory *kmsmem;
GstMemory *mem;
gint i, ret;
+ guint32 gem_handle[4] = { 0, };
g_return_val_if_fail (n_planes <= GST_VIDEO_MAX_PLANES, FALSE);
@@ -508,42 +443,41 @@ gst_kms_allocator_dmabuf_import (GstAllocator * allocator, gint * prime_fds,
alloc = GST_KMS_ALLOCATOR (allocator);
for (i = 0; i < n_planes; i++) {
- ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i],
- &kmsmem->gem_handle[i]);
+ ret = drmPrimeFDToHandle (alloc->priv->fd, prime_fds[i], &gem_handle[i]);
if (ret)
goto import_fd_failed;
}
- if (!gst_kms_allocator_add_fb (alloc, kmsmem, offsets, vinfo))
+ if (!gst_kms_allocator_add_fb (alloc, kmsmem, offsets, vinfo, gem_handle))
goto failed;
+done:
for (i = 0; i < n_planes; i++) {
- struct drm_gem_close arg = { kmsmem->gem_handle[i], };
+ struct drm_gem_close arg = { gem_handle[i], };
gint err;
+ if (!gem_handle[i])
+ continue;
+
err = drmIoctl (alloc->priv->fd, DRM_IOCTL_GEM_CLOSE, &arg);
if (err)
GST_WARNING_OBJECT (allocator,
"Failed to close GEM handle: %s %d", g_strerror (errno), errno);
-
- kmsmem->gem_handle[i] = 0;
}
return kmsmem;
/* ERRORS */
import_fd_failed:
- {
- GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)",
- prime_fds[i], g_strerror (errno), errno);
- /* fallback */
- }
+ GST_ERROR_OBJECT (alloc, "Failed to import prime fd %d: %s (%d)",
+ prime_fds[i], g_strerror (errno), errno);
+ /* fallthrough */
failed:
- {
- gst_memory_unref (mem);
- return NULL;
- }
+ gst_memory_unref (mem);
+ mem = NULL;
+ kmsmem = NULL;
+ goto done;
}
GstMemory *
@@ -552,30 +486,21 @@ gst_kms_allocator_dmabuf_export (GstAllocator * allocator, GstMemory * _kmsmem)
GstKMSMemory *kmsmem = (GstKMSMemory *) _kmsmem;
GstKMSAllocator *alloc = GST_KMS_ALLOCATOR (allocator);
GstMemory *mem;
- gint ret;
- gint prime_fd;
/* We can only export DUMB buffers */
g_return_val_if_fail (kmsmem->bo, NULL);
-
- ret = drmPrimeHandleToFD (alloc->priv->fd, kmsmem->bo->handle,
- DRM_CLOEXEC | DRM_RDWR, &prime_fd);
- if (ret)
+ mem = gst_drm_dumb_memory_export_dmabuf (kmsmem->bo);
+ if (!mem)
goto export_fd_failed;
- if (G_UNLIKELY (alloc->priv->dmabuf_alloc == NULL))
- alloc->priv->dmabuf_alloc = gst_dmabuf_allocator_new ();
-
- mem = gst_dmabuf_allocator_alloc (alloc->priv->dmabuf_alloc, prime_fd,
- gst_memory_get_sizes (_kmsmem, NULL, NULL));
-
/* Populate the cache so KMSSink can find the kmsmem back when it receives
* one of these DMABuf. This call takes ownership of the kmsmem. */
gst_kms_allocator_cache (allocator, mem, _kmsmem);
- GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d", kmsmem->bo->handle,
- prime_fd);
+ GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d",
+ gst_drm_dumb_memory_get_handle (kmsmem->bo),
+ gst_dmabuf_memory_get_fd (mem));
return mem;
@@ -583,7 +508,7 @@ gst_kms_allocator_dmabuf_export (GstAllocator * allocator, GstMemory * _kmsmem)
export_fd_failed:
{
GST_ERROR_OBJECT (alloc, "Failed to export bo handle %d: %s (%d)",
- kmsmem->bo->handle, g_strerror (errno), ret);
+ gst_drm_dumb_memory_get_handle (kmsmem->bo), g_strerror (errno), errno);
return NULL;
}
}
diff --git a/sys/kms/gstkmsallocator.h b/sys/kms/gstkmsallocator.h
index 9d00126..258efba 100644
--- a/sys/kms/gstkmsallocator.h
+++ b/sys/kms/gstkmsallocator.h
@@ -56,8 +56,9 @@ struct _GstKMSMemory
GstMemory parent;
guint32 fb_id;
- guint32 gem_handle[GST_VIDEO_MAX_PLANES];
- struct kms_bo *bo;
+ GstMemory *bo;
+ GstMapInfo bo_map;
+ gint bo_map_refs;
};
struct _GstKMSAllocator
--
2.25.1

View File

@@ -0,0 +1,49 @@
From 930fb463ebf2fffed6a8e8f8b05594082fb2a3c2 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 15 Feb 2023 11:49:38 -0500
Subject: [PATCH 02/17] gtkwaylandsink: Remove redefine of
GST_CAPS_FEATURE_MEMORY_DMABUF
Instead just include the appropriate header file. There is no meson deps
modification as gstallocators_dep is already part of gstwayland_dep.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index e4dd06e..35367ca 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -25,13 +25,13 @@
#endif
#include "gstgtkwaylandsink.h"
+#include "gstgtkutils.h"
+#include "gtkgstwaylandwidget.h"
#include <gdk/gdk.h>
+#include <gst/allocators/allocators.h>
#include <gst/wayland/wayland.h>
-#include "gstgtkutils.h"
-#include "gtkgstwaylandwidget.h"
-
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#else
@@ -41,10 +41,6 @@
#define GST_CAT_DEFAULT gst_debug_gtk_wayland_sink
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
-#ifndef GST_CAPS_FEATURE_MEMORY_DMABUF
-#define GST_CAPS_FEATURE_MEMORY_DMABUF "memory:DMABuf"
-#endif
-
#define WL_VIDEO_FORMATS \
"{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, " \
"RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, NV61, " \
--
2.25.1

View File

@@ -0,0 +1,156 @@
From 4b6f23a997485355aeca22193c317184fe74a28c Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 15 Feb 2023 12:13:16 -0500
Subject: [PATCH 03/17] waylandsink: Stop modifying the display GstVideoInfo
The video_info is supposed to match the display dimentions, but as soon as we
get a padded video buffer, we modify it. This has side effect later on and
maybe cause bad frames.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 22 +++++++++-------------
ext/wayland/gstwaylandsink.c | 18 +++++++-----------
2 files changed, 16 insertions(+), 24 deletions(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 35367ca..dc1e3c3 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -982,7 +982,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GstWlBuffer *wlbuffer;
GstVideoMeta *vmeta;
GstVideoFormat format;
- GstVideoInfo old_vinfo;
+ GstVideoInfo src_vinfo;
GstMemory *mem;
struct wl_buffer *wbuf = NULL;
@@ -1023,23 +1023,23 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
/* update video info from video meta */
mem = gst_buffer_peek_memory (buffer, 0);
- old_vinfo = priv->video_info;
+ src_vinfo = priv->video_info;
vmeta = gst_buffer_get_video_meta (buffer);
if (vmeta) {
gint i;
for (i = 0; i < vmeta->n_planes; i++) {
- priv->video_info.offset[i] = vmeta->offset[i];
- priv->video_info.stride[i] = vmeta->stride[i];
+ src_vinfo.offset[i] = vmeta->offset[i];
+ src_vinfo.stride[i] = vmeta->stride[i];
}
- priv->video_info.size = gst_buffer_get_size (buffer);
+ src_vinfo.size = gst_buffer_get_size (buffer);
}
GST_LOG_OBJECT (self,
"buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our "
"display, creating it", buffer);
- format = GST_VIDEO_INFO_FORMAT (&priv->video_info);
+ format = GST_VIDEO_INFO_FORMAT (&src_vinfo);
if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) {
guint i, nb_dmabuf = 0;
@@ -1049,21 +1049,17 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display,
- &priv->video_info);
+ &src_vinfo);
}
if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,
- &priv->video_info);
+ &src_vinfo);
/* If nothing worked, copy into our internal pool */
if (!wbuf) {
GstVideoFrame src, dst;
- GstVideoInfo src_info = priv->video_info;
-
- /* rollback video info changes */
- priv->video_info = old_vinfo;
/* we don't know how to create a wl_buffer directly from the provided
* memory, so we have to copy the data to shm memory that we know how
@@ -1115,7 +1111,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GST_MAP_WRITE))
goto dst_map_failed;
- if (!gst_video_frame_map (&src, &src_info, buffer, GST_MAP_READ)) {
+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 2f116bf..6dd0807 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -741,7 +741,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GstWlBuffer *wlbuffer;
GstVideoMeta *vmeta;
GstVideoFormat format;
- GstVideoInfo old_vinfo;
+ GstVideoInfo src_vinfo;
GstMemory *mem;
struct wl_buffer *wbuf = NULL;
@@ -794,16 +794,16 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
/* update video info from video meta */
mem = gst_buffer_peek_memory (buffer, 0);
- old_vinfo = self->video_info;
+ src_vinfo = self->video_info;
vmeta = gst_buffer_get_video_meta (buffer);
if (vmeta) {
gint i;
for (i = 0; i < vmeta->n_planes; i++) {
- self->video_info.offset[i] = vmeta->offset[i];
- self->video_info.stride[i] = vmeta->stride[i];
+ src_vinfo.offset[i] = vmeta->offset[i];
+ src_vinfo.stride[i] = vmeta->stride[i];
}
- self->video_info.size = gst_buffer_get_size (buffer);
+ src_vinfo.size = gst_buffer_get_size (buffer);
}
GST_LOG_OBJECT (self,
@@ -820,7 +820,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display,
- &self->video_info);
+ &src_vinfo);
}
if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) {
@@ -831,10 +831,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
/* If nothing worked, copy into our internal pool */
if (!wbuf) {
GstVideoFrame src, dst;
- GstVideoInfo src_info = self->video_info;
-
- /* rollback video info changes */
- self->video_info = old_vinfo;
/* we don't know how to create a wl_buffer directly from the provided
* memory, so we have to copy the data to shm memory that we know how
@@ -886,7 +882,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GST_MAP_WRITE))
goto dst_map_failed;
- if (!gst_video_frame_map (&src, &src_info, buffer, GST_MAP_READ)) {
+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
--
2.25.1

View File

@@ -0,0 +1,39 @@
From baf873c06399c6ad6062cc1a96cc1b0bfc0edf3c Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 15 Feb 2023 12:34:27 -0500
Subject: [PATCH 04/17] gtkwaylandsink: Force a redraw on resolution change
As we don't render into the widget directly, there is no "initial" draw
happening. As a side effect, the internal aspect ratio adapted display
width/height is never initialize leading to assertions when handling navigation
events.
gst_video_center_rect: assertion 'src->h != 0' failed
Simply queue a redraw after setting the widget format in order to fix the issue.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index dc1e3c3..5288614 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -875,6 +875,12 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
GST_OBJECT_UNLOCK (self);
return FALSE;
}
+
+ /* Ensure queue_draw get executed and internal display size get initialized.
+ * This does not happen otherwise as we don't draw in the widget
+ */
+ gtk_gst_base_widget_queue_draw (GTK_GST_BASE_WIDGET (priv->gtk_widget));
+
GST_OBJECT_UNLOCK (self);
priv->use_dmabuf = use_dmabuf;
--
2.25.1

View File

@@ -0,0 +1,50 @@
From 9084a960a565ed80d34c41c7b0ab4f4a716a4f54 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 15 Feb 2023 13:11:36 -0500
Subject: [PATCH] waylandsink: Let the baseclass know when frames are dropped
This is using the new GST_BASE_SINK_FLOW_DROPPED return value.
With this change, fpsdisplaysink will properly report the
render and dropped rate.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 2 ++
ext/wayland/gstwaylandsink.c | 1 +
2 files changed, 3 insertions(+)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 5288614bd7..acc32882ce 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -1006,6 +1006,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (!priv->wl_window) {
GST_LOG_OBJECT (self,
"buffer %" GST_PTR_FORMAT " dropped (waiting for window)", buffer);
+ ret = GST_BASE_SINK_FLOW_DROPPED;
goto done;
}
@@ -1013,6 +1014,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (priv->redraw_pending) {
GST_LOG_OBJECT (self, "buffer %" GST_PTR_FORMAT " dropped (redraw pending)",
buffer);
+ ret = GST_BASE_SINK_FLOW_DROPPED;
goto done;
}
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 6dd0807820..bbbcb93932 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -773,6 +773,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (self->redraw_pending) {
GST_LOG_OBJECT (self, "buffer %" GST_PTR_FORMAT " dropped (redraw pending)",
buffer);
+ ret = GST_BASE_SINK_FLOW_DROPPED;
goto done;
}
--
2.25.1

View File

@@ -0,0 +1,348 @@
From 79405fb8592f22d62b4faa2f7098b814cc2a0d49 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Thu, 16 Feb 2023 13:54:42 -0500
Subject: [PATCH 06/17] waylandsink: Refactor internal pool handling
This is to make it easier to support more then one allocators
including falling back from one to another.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 101 +++++++++++++++++++++--------------
ext/wayland/gstwaylandsink.c | 101 +++++++++++++++++++++--------------
ext/wayland/gstwaylandsink.h | 1 +
3 files changed, 124 insertions(+), 79 deletions(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 1afad32..8967d97 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -109,6 +109,7 @@ typedef struct _GstGtkWaylandSinkPrivate
gboolean video_info_changed;
GstVideoInfo video_info;
+ GstCaps *caps;
gboolean redraw_pending;
GMutex render_lock;
@@ -200,6 +201,7 @@ gst_gtk_wayland_sink_finalize (GObject * object)
gst_gtk_wayland_sink_get_instance_private (self);
g_clear_object (&priv->gtk_widget);
+ gst_clear_caps (&priv->caps);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -801,30 +803,57 @@ gst_gtk_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
return caps;
}
-static GstBufferPool *
-gst_gtk_wayland_create_pool (GstGtkWaylandSink * self, GstCaps * caps)
+static gboolean
+gst_gtk_wayland_update_pool (GstGtkWaylandSink * self, GstAllocator * allocator)
{
GstGtkWaylandSinkPrivate *priv =
gst_gtk_wayland_sink_get_instance_private (self);
- GstBufferPool *pool = NULL;
- GstStructure *structure;
gsize size = priv->video_info.size;
- GstAllocator *alloc;
+ GstStructure *config;
- pool = gst_wl_video_buffer_pool_new ();
+ /* Pools with outstanding buffer cannot be reconfigured, so we must use
+ * a new pool. */
+ if (priv->pool) {
+ gst_buffer_pool_set_active (priv->pool, FALSE);
+ gst_object_unref (priv->pool);
+ }
+ priv->pool = gst_wl_video_buffer_pool_new ();
- structure = gst_buffer_pool_get_config (pool);
- gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
+ config = gst_buffer_pool_get_config (priv->pool);
+ gst_buffer_pool_config_set_params (config, priv->caps, size, 2, 0);
+ gst_buffer_pool_config_set_allocator (config, allocator, NULL);
- alloc = gst_wl_shm_allocator_get ();
- gst_buffer_pool_config_set_allocator (structure, alloc, NULL);
- if (!gst_buffer_pool_set_config (pool, structure)) {
- g_object_unref (pool);
- pool = NULL;
+ if (!gst_buffer_pool_set_config (priv->pool, config))
+ return FALSE;
+
+ return gst_buffer_pool_set_active (priv->pool, TRUE);
+}
+
+static gboolean
+gst_gtk_wayland_activate_shm_pool (GstGtkWaylandSink * self)
+{
+ GstGtkWaylandSinkPrivate *priv =
+ gst_gtk_wayland_sink_get_instance_private (self);
+ GstAllocator *alloc = NULL;
+
+ if (priv->pool && gst_buffer_pool_is_active (priv->pool)) {
+ GstStructure *config = gst_buffer_pool_get_config (priv->pool);
+ gboolean is_shm = FALSE;
+
+ if (gst_buffer_pool_config_get_allocator (config, &alloc, NULL) && alloc)
+ is_shm = GST_IS_WL_SHM_ALLOCATOR (alloc);
+
+ gst_structure_free (config);
+
+ if (is_shm)
+ return TRUE;
}
- g_object_unref (alloc);
- return pool;
+ alloc = gst_wl_shm_allocator_get ();
+ gst_gtk_wayland_update_pool (self, alloc);
+ gst_object_unref (alloc);
+
+ return TRUE;
}
static gboolean
@@ -845,10 +874,11 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&priv->video_info);
priv->video_info_changed = TRUE;
- /* create a new pool for the new caps */
- if (priv->pool)
- gst_object_unref (priv->pool);
- priv->pool = gst_gtk_wayland_create_pool (self, caps);
+ /* free pooled buffer used with previous caps */
+ if (priv->pool) {
+ gst_buffer_pool_set_active (priv->pool, FALSE);
+ gst_clear_object (&priv->pool);
+ }
use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0),
GST_CAPS_FEATURE_MEMORY_DMABUF);
@@ -884,6 +914,8 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
GST_OBJECT_UNLOCK (self);
priv->use_dmabuf = use_dmabuf;
+ /* Will be used to create buffer pools */
+ gst_caps_replace (&priv->caps, caps);
return TRUE;
@@ -914,8 +946,14 @@ gst_gtk_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
gst_query_parse_allocation (query, &caps, &need_pool);
- if (need_pool)
- pool = gst_gtk_wayland_create_pool (self, caps);
+ if (need_pool) {
+ GstStructure *config;
+ pool = gst_wl_video_buffer_pool_new ();
+ config = gst_buffer_pool_get_config (pool);
+ gst_buffer_pool_config_set_allocator (config,
+ gst_wl_shm_allocator_get (), NULL);
+ gst_buffer_pool_set_config (pool, config);
+ }
gst_query_add_allocation_pool (query, pool, priv->video_info.size, 2, 0);
if (pool)
@@ -1077,25 +1115,10 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
"buffer %" GST_PTR_FORMAT " cannot have a wl_buffer, "
"copying to wl_shm memory", buffer);
- /* priv->pool always exists (created in set_caps), but it may not
- * be active if upstream is not using it */
- if (!gst_buffer_pool_is_active (priv->pool)) {
- GstStructure *config;
- GstCaps *caps;
- config = gst_buffer_pool_get_config (priv->pool);
- gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL);
-
- /* revert back to default strides and offsets */
- gst_video_info_from_caps (&priv->video_info, caps);
- gst_buffer_pool_config_set_params (config, caps, priv->video_info.size,
- 2, 0);
-
- /* This is a video pool, it should not fail with basic settings */
- if (!gst_buffer_pool_set_config (priv->pool, config) ||
- !gst_buffer_pool_set_active (priv->pool, TRUE))
- goto activate_failed;
- }
+ /* ensure the internal pool is configured for SHM */
+ if (!gst_gtk_wayland_activate_shm_pool (self))
+ goto activate_failed;
ret = gst_buffer_pool_acquire_buffer (priv->pool, &to_render, NULL);
if (ret != GST_FLOW_OK)
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 7077ee9..b9b6267 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -315,6 +315,8 @@ gst_wayland_sink_finalize (GObject * object)
if (self->pool)
gst_object_unref (self->pool);
+ gst_clear_caps (&self->caps);
+
g_free (self->display_name);
g_mutex_clear (&self->display_lock);
@@ -578,28 +580,53 @@ gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
return caps;
}
-static GstBufferPool *
-gst_wayland_create_pool (GstWaylandSink * self, GstCaps * caps)
+static gboolean
+gst_wayland_update_pool (GstWaylandSink * self, GstAllocator * allocator)
{
- GstBufferPool *pool = NULL;
- GstStructure *structure;
gsize size = self->video_info.size;
- GstAllocator *alloc;
+ GstStructure *config;
- pool = gst_wl_video_buffer_pool_new ();
+ /* Pools with outstanding buffer cannot be reconfigured, so we must use
+ * a new pool. */
+ if (self->pool) {
+ gst_buffer_pool_set_active (self->pool, FALSE);
+ gst_object_unref (self->pool);
+ }
+ self->pool = gst_wl_video_buffer_pool_new ();
- structure = gst_buffer_pool_get_config (pool);
- gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
+ config = gst_buffer_pool_get_config (self->pool);
+ gst_buffer_pool_config_set_params (config, self->caps, size, 2, 0);
+ gst_buffer_pool_config_set_allocator (config, allocator, NULL);
- alloc = gst_wl_shm_allocator_get ();
- gst_buffer_pool_config_set_allocator (structure, alloc, NULL);
- if (!gst_buffer_pool_set_config (pool, structure)) {
- g_object_unref (pool);
- pool = NULL;
+ if (!gst_buffer_pool_set_config (self->pool, config))
+ return FALSE;
+
+ return gst_buffer_pool_set_active (self->pool, TRUE);
+}
+
+static gboolean
+gst_wayland_activate_shm_pool (GstWaylandSink * self)
+{
+ GstAllocator *alloc = NULL;
+
+ if (self->pool && gst_buffer_pool_is_active (self->pool)) {
+ GstStructure *config = gst_buffer_pool_get_config (self->pool);
+ gboolean is_shm = FALSE;
+
+ if (gst_buffer_pool_config_get_allocator (config, &alloc, NULL) && alloc)
+ is_shm = GST_IS_WL_SHM_ALLOCATOR (alloc);
+
+ gst_structure_free (config);
+
+ if (is_shm)
+ return TRUE;
}
- g_object_unref (alloc);
- return pool;
+ alloc = gst_wl_shm_allocator_get ();
+ gst_wayland_update_pool (self, alloc);
+ gst_object_unref (alloc);
+
+ return TRUE;
}
static gboolean
@@ -618,10 +645,11 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&self->video_info);
self->video_info_changed = TRUE;
- /* create a new pool for the new caps */
- if (self->pool)
- gst_object_unref (self->pool);
- self->pool = gst_wayland_create_pool (self, caps);
+ /* free pooled buffer used with previous caps */
+ if (self->pool) {
+ gst_buffer_pool_set_active (self->pool, FALSE);
+ gst_clear_object (&self->pool);
+ }
use_dmabuf = gst_caps_features_contains (gst_caps_get_features (caps, 0),
GST_CAPS_FEATURE_MEMORY_DMABUF);
@@ -639,6 +667,9 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
self->use_dmabuf = use_dmabuf;
+ /* Will be used to create buffer pools */
+ gst_caps_replace (&self->caps, caps);
+
return TRUE;
invalid_format:
@@ -666,8 +697,14 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
gst_query_parse_allocation (query, &caps, &need_pool);
- if (need_pool)
- pool = gst_wayland_create_pool (self, caps);
+ if (need_pool) {
+ GstStructure *config;
+ pool = gst_wl_video_buffer_pool_new ();
+ config = gst_buffer_pool_get_config (pool);
+ gst_buffer_pool_config_set_allocator (config,
+ gst_wl_shm_allocator_get (), NULL);
+ gst_buffer_pool_set_config (pool, config);
+ }
gst_query_add_allocation_pool (query, pool, self->video_info.size, 2, 0);
if (pool)
@@ -844,25 +881,9 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
"buffer %" GST_PTR_FORMAT " cannot have a wl_buffer, "
"copying to wl_shm memory", buffer);
- /* self->pool always exists (created in set_caps), but it may not
- * be active if upstream is not using it */
- if (!gst_buffer_pool_is_active (self->pool)) {
- GstStructure *config;
- GstCaps *caps;
-
- config = gst_buffer_pool_get_config (self->pool);
- gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL);
-
- /* revert back to default strides and offsets */
- gst_video_info_from_caps (&self->video_info, caps);
- gst_buffer_pool_config_set_params (config, caps, self->video_info.size,
- 2, 0);
-
- /* This is a video pool, it should not fail with basic settings */
- if (!gst_buffer_pool_set_config (self->pool, config) ||
- !gst_buffer_pool_set_active (self->pool, TRUE))
- goto activate_failed;
- }
+ /* ensure the internal pool is configured for SHM */
+ if (!gst_wayland_activate_shm_pool (self))
+ goto activate_failed;
ret = gst_buffer_pool_acquire_buffer (self->pool, &to_render, NULL);
if (ret != GST_FLOW_OK)
diff --git a/ext/wayland/gstwaylandsink.h b/ext/wayland/gstwaylandsink.h
index 3243d8c..1ff50d1 100644
--- a/ext/wayland/gstwaylandsink.h
+++ b/ext/wayland/gstwaylandsink.h
@@ -56,6 +56,7 @@ struct _GstWaylandSink
gboolean video_info_changed;
GstVideoInfo video_info;
gboolean fullscreen;
+ GstCaps *caps;
gchar *display_name;
--
2.25.1

View File

@@ -0,0 +1,30 @@
From 60b5f9809703c6668e566a8133b000ec2c3c0dfc Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Thu, 16 Feb 2023 14:31:10 -0500
Subject: [PATCH 07/17] gtkwaylandsink: Fix display/wl_window/pool leaks
These were leaked in the GTK implementation of the sink.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 8967d97..0c74a91 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -200,6 +200,10 @@ gst_gtk_wayland_sink_finalize (GObject * object)
GstGtkWaylandSinkPrivate *priv =
gst_gtk_wayland_sink_get_instance_private (self);
+ g_clear_object (&priv->display);
+ g_clear_object (&priv->wl_window);
+ g_clear_object (&priv->pool);
+
g_clear_object (&priv->gtk_widget);
gst_clear_caps (&priv->caps);
--
2.25.1

View File

@@ -0,0 +1,173 @@
From 38ee1f8e97429506db0ca9c0c2ac61da4f00ba5d Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Thu, 16 Feb 2023 21:12:08 -0500
Subject: [PATCH 08/17] wllinuxdmabuf: Handle video meta inside the importer
This allow simplifying the GstVideoInfo handling in the sinks. Instead
of having to update a video info for the import, the sink can simply pass the
video info associated with the caps and rely on the VideoMeta in the GstBuffer
to obtain the appropriate offset and stride.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 22 ++++------------------
ext/wayland/gstwaylandsink.c | 18 ++----------------
gst-libs/gst/wayland/gstwllinuxdmabuf.c | 13 +++++++++++--
3 files changed, 17 insertions(+), 36 deletions(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 0c74a91..c46207c 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -1028,9 +1028,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
gst_gtk_wayland_sink_get_instance_private (self);
GstBuffer *to_render;
GstWlBuffer *wlbuffer;
- GstVideoMeta *vmeta;
GstVideoFormat format;
- GstVideoInfo src_vinfo;
GstMemory *mem;
struct wl_buffer *wbuf = NULL;
@@ -1073,23 +1071,11 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
/* update video info from video meta */
mem = gst_buffer_peek_memory (buffer, 0);
- src_vinfo = priv->video_info;
- vmeta = gst_buffer_get_video_meta (buffer);
- if (vmeta) {
- gint i;
-
- for (i = 0; i < vmeta->n_planes; i++) {
- src_vinfo.offset[i] = vmeta->offset[i];
- src_vinfo.stride[i] = vmeta->stride[i];
- }
- src_vinfo.size = gst_buffer_get_size (buffer);
- }
-
GST_LOG_OBJECT (self,
"buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our "
"display, creating it", buffer);
- format = GST_VIDEO_INFO_FORMAT (&src_vinfo);
+ format = GST_VIDEO_INFO_FORMAT (&priv->video_info);
if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) {
guint i, nb_dmabuf = 0;
@@ -1099,13 +1085,13 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display,
- &src_vinfo);
+ &priv->video_info);
}
if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,
- &src_vinfo);
+ &priv->video_info);
/* If nothing worked, copy into our internal pool */
if (!wbuf) {
@@ -1146,7 +1132,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GST_MAP_WRITE))
goto dst_map_failed;
- if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) {
+ if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index b9b6267..aa39447 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -779,9 +779,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GstWaylandSink *self = GST_WAYLAND_SINK (vsink);
GstBuffer *to_render;
GstWlBuffer *wlbuffer;
- GstVideoMeta *vmeta;
GstVideoFormat format;
- GstVideoInfo src_vinfo;
GstMemory *mem;
struct wl_buffer *wbuf = NULL;
@@ -835,18 +833,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
/* update video info from video meta */
mem = gst_buffer_peek_memory (buffer, 0);
- src_vinfo = self->video_info;
- vmeta = gst_buffer_get_video_meta (buffer);
- if (vmeta) {
- gint i;
-
- for (i = 0; i < vmeta->n_planes; i++) {
- src_vinfo.offset[i] = vmeta->offset[i];
- src_vinfo.stride[i] = vmeta->stride[i];
- }
- src_vinfo.size = gst_buffer_get_size (buffer);
- }
-
GST_LOG_OBJECT (self,
"buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our "
"display, creating it", buffer);
@@ -861,7 +847,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display,
- &src_vinfo);
+ &self->video_info);
}
if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) {
@@ -907,7 +893,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
GST_MAP_WRITE))
goto dst_map_failed;
- if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) {
+ if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) {
gst_video_frame_unmap (&dst);
goto src_map_failed;
}
diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c
index d6ee6ec..4b33d05 100644
--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c
+++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c
@@ -88,6 +88,9 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf,
GstMemory *mem;
int format;
guint i, width, height;
+ const gsize *offsets = info->offset;
+ const gint *strides = info->stride;
+ GstVideoMeta *vmeta;
guint nplanes, flags = 0;
struct zwp_linux_buffer_params_v1 *params;
gint64 timeout;
@@ -107,6 +110,12 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf,
height = GST_VIDEO_INFO_HEIGHT (info);
nplanes = GST_VIDEO_INFO_N_PLANES (info);
+ vmeta = gst_buffer_get_video_meta (buf);
+ if (vmeta) {
+ offsets = vmeta->offset;
+ strides = vmeta->stride;
+ }
+
GST_DEBUG_OBJECT (display, "Creating wl_buffer from DMABuf of size %"
G_GSSIZE_FORMAT " (%d x %d), format %s", info->size, width, height,
gst_wl_dmabuf_format_to_string (format));
@@ -119,8 +128,8 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf,
guint offset, stride, mem_idx, length;
gsize skip;
- offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i);
- stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
+ offset = offsets[i];
+ stride = strides[i];
if (gst_buffer_find_memory (buf, offset, 1, &mem_idx, &length, &skip)) {
GstMemory *m = gst_buffer_peek_memory (buf, mem_idx);
gint fd = gst_dmabuf_memory_get_fd (m);
--
2.25.1

View File

@@ -0,0 +1,92 @@
From d9b34f66193a5a4ac60df33ec80e0da76d197fe4 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Fri, 17 Feb 2023 09:42:42 -0500
Subject: [PATCH 09/17] wlvideoformat: Fix sign issue for DRM fourcc
DRM fourcc ared defined as 32bit unsigned in, but the format helper was passing
an int, while using a unsigned int internally. This is a API/ABI break, but
the API is still unstable.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
gst-libs/gst/wayland/gstwldisplay.c | 2 +-
gst-libs/gst/wayland/gstwllinuxdmabuf.c | 4 ++--
gst-libs/gst/wayland/gstwlvideoformat.c | 6 +++---
gst-libs/gst/wayland/gstwlvideoformat.h | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/gst-libs/gst/wayland/gstwldisplay.c b/gst-libs/gst/wayland/gstwldisplay.c
index 1f83cb2..a9da274 100644
--- a/gst-libs/gst/wayland/gstwldisplay.c
+++ b/gst-libs/gst/wayland/gstwldisplay.c
@@ -226,7 +226,7 @@ gst_wl_display_check_format_for_dmabuf (GstWlDisplay * self,
return FALSE;
dmabuf_fmt = gst_video_format_to_wl_dmabuf_format (format);
- if (dmabuf_fmt == (guint) - 1)
+ if (!dmabuf_fmt)
return FALSE;
formats = priv->dmabuf_formats;
diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c
index 4b33d05..deb5d32 100644
--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c
+++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c
@@ -175,8 +175,8 @@ out:
GST_ERROR_OBJECT (mem->allocator, "can't create linux-dmabuf buffer");
} else {
GST_DEBUG_OBJECT (mem->allocator, "created linux_dmabuf wl_buffer (%p):"
- "%dx%d, fmt=%.4s, %d planes",
- data.wbuf, width, height, (char *) &format, nplanes);
+ "%dx%d, fmt=%" GST_FOURCC_FORMAT ", %d planes",
+ data.wbuf, width, height, GST_FOURCC_ARGS (format), nplanes);
}
g_mutex_unlock (&data.lock);
diff --git a/gst-libs/gst/wayland/gstwlvideoformat.c b/gst-libs/gst/wayland/gstwlvideoformat.c
index 44a9536..49d927a 100644
--- a/gst-libs/gst/wayland/gstwlvideoformat.c
+++ b/gst-libs/gst/wayland/gstwlvideoformat.c
@@ -48,7 +48,7 @@ gst_wl_videoformat_init_once (void)
typedef struct
{
enum wl_shm_format wl_shm_format;
- guint dma_format;
+ guint32 dma_format;
GstVideoFormat gst_format;
} wl_VideoFormat;
@@ -96,7 +96,7 @@ gst_video_format_to_wl_shm_format (GstVideoFormat format)
return -1;
}
-gint
+guint32
gst_video_format_to_wl_dmabuf_format (GstVideoFormat format)
{
guint i;
@@ -106,7 +106,7 @@ gst_video_format_to_wl_dmabuf_format (GstVideoFormat format)
return wl_formats[i].dma_format;
GST_WARNING ("wayland dmabuf video format not found");
- return -1;
+ return 0;
}
GstVideoFormat
diff --git a/gst-libs/gst/wayland/gstwlvideoformat.h b/gst-libs/gst/wayland/gstwlvideoformat.h
index bc36a08..bbacde3 100644
--- a/gst-libs/gst/wayland/gstwlvideoformat.h
+++ b/gst-libs/gst/wayland/gstwlvideoformat.h
@@ -36,7 +36,7 @@ GST_WL_API
enum wl_shm_format gst_video_format_to_wl_shm_format (GstVideoFormat format);
GST_WL_API
-gint gst_video_format_to_wl_dmabuf_format (GstVideoFormat format);
+guint32 gst_video_format_to_wl_dmabuf_format (GstVideoFormat format);
GST_WL_API
GstVideoFormat gst_wl_shm_format_to_video_format (enum wl_shm_format wl_format);
--
2.25.1

View File

@@ -0,0 +1,267 @@
From c5c879049ee10ff0f1a54b90cbb317bca09933a6 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 13:09:09 +0200
Subject: [PATCH 10/17] wlvideobufferpool: Add DRM Dumb buffer support
This allow the wayland buffer pool to use a GstDRMDumbAllocator
if it has been configured to do so.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
gst-libs/gst/wayland/gstwlvideobufferpool.c | 200 +++++++++++++++++++-
gst-libs/gst/wayland/gstwlvideobufferpool.h | 10 +-
2 files changed, 200 insertions(+), 10 deletions(-)
diff --git a/gst-libs/gst/wayland/gstwlvideobufferpool.c b/gst-libs/gst/wayland/gstwlvideobufferpool.c
index b367d9f..3292056 100644
--- a/gst-libs/gst/wayland/gstwlvideobufferpool.c
+++ b/gst-libs/gst/wayland/gstwlvideobufferpool.c
@@ -24,8 +24,198 @@
#include "gstwlvideobufferpool.h"
-G_DEFINE_TYPE (GstWlVideoBufferPool, gst_wl_video_buffer_pool,
- GST_TYPE_VIDEO_BUFFER_POOL);
+#include <gst/allocators/allocators.h>
+#include <drm_fourcc.h>
+
+GST_DEBUG_CATEGORY (gst_wl_videobufferpool_debug);
+#define GST_CAT_DEFAULT gst_wl_videobufferpool_debug
+
+struct _GstWlVideoBufferPool
+{
+ GstVideoBufferPool parent;
+ GstAllocator *allocator;
+ GstVideoInfo vinfo;
+};
+
+#define gst_wl_video_buffer_pool_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstWlVideoBufferPool, gst_wl_video_buffer_pool,
+ GST_TYPE_VIDEO_BUFFER_POOL,
+ GST_DEBUG_CATEGORY_INIT (gst_wl_videobufferpool_debug,
+ "wl_videobufferpool", 0, "wl_dmabuf library"));
+
+static void
+gst_wl_update_video_info_from_pitch (GstVideoInfo * vinfo, gint n_planes,
+ guint32 pitch)
+{
+ gint i;
+ gsize offs = 0;
+ guint32 height = GST_VIDEO_INFO_HEIGHT (vinfo);
+
+ if (!pitch)
+ return;
+
+ for (i = 0; i < n_planes; i++) {
+ guint32 plane_pitch;
+
+ /* Overwrite the video info's stride and offset using the pitch calculcated
+ * by the kms driver. */
+ plane_pitch = gst_video_format_info_extrapolate_stride (vinfo->finfo, i,
+ pitch);
+ GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = plane_pitch;
+ GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = offs;
+
+ /* Note that we cannot negotiate special padding betweem each planes,
+ * hence using the display height here. */
+ offs += plane_pitch
+ * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo->finfo, i, height);
+
+ GST_DEBUG ("Updated plane %i with stride %i and "
+ "offset %" G_GSIZE_FORMAT "(from pitch %u)", i,
+ GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i),
+ GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i), pitch);
+ }
+
+ /* Update with the size use for display, excluding any padding at the end */
+ GST_VIDEO_INFO_SIZE (vinfo) = offs;
+}
+
+static gboolean
+gst_wl_video_buffer_pool_start (GstBufferPool * pool)
+{
+ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool);
+ GstStructure *config = gst_buffer_pool_get_config (pool);
+ GstCaps *caps;
+ GstAllocator *allocator;
+ gboolean ret = FALSE;
+ GstVideoFormat format;
+
+ if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
+ goto wrong_config;
+
+ if (!gst_buffer_pool_config_get_allocator (config, &allocator, NULL))
+ goto wrong_config;
+
+ /* now parse the caps from the config */
+ if (!gst_video_info_from_caps (&self->vinfo, caps))
+ goto wrong_caps;
+
+ format = GST_VIDEO_INFO_FORMAT (&self->vinfo);
+ if (!gst_video_format_to_wl_dmabuf_format (format))
+ goto unsupported_pixel_format;
+
+ if (GST_IS_DRM_DUMB_ALLOCATOR (allocator)) {
+ if (!gst_drm_dumb_allocator_has_prime_export (allocator))
+ goto no_prime_export;
+
+ self->allocator = gst_object_ref (allocator);
+ }
+
+ /* all good */
+ ret = GST_BUFFER_POOL_CLASS (parent_class)->start (pool);
+
+done:
+ gst_structure_free (config);
+ return ret;
+
+wrong_config:
+ {
+ GST_WARNING_OBJECT (pool, "invalid config");
+ goto done;
+ }
+wrong_caps:
+ {
+ GST_WARNING_OBJECT (pool,
+ "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+ goto done;
+ }
+unsupported_pixel_format:
+ {
+ GST_WARNING_OBJECT (pool, "no support for %s pixel format",
+ gst_video_format_to_string (format));
+ goto done;
+ }
+no_prime_export:
+ {
+ GST_WARNING_OBJECT (self,
+ "not using DRM Dumb allocator as it can't export DMABuf.");
+ goto done;
+ }
+}
+
+static gboolean
+gst_wl_video_buffer_pool_stop (GstBufferPool * pool)
+{
+ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool);
+ gst_clear_object (&self->allocator);
+ gst_video_info_init (&self->vinfo);
+ return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool);
+}
+
+static GstFlowReturn
+gst_wl_video_buffer_pool_alloc_buffer (GstBufferPool * pool,
+ GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
+{
+ GstWlVideoBufferPool *self = GST_WL_VIDEO_BUFFER_POOL (pool);
+ guint32 pitch;
+ GstBuffer *buf;
+ GstMemory *drm_mem, *dma_mem;
+ gsize dumbbuf_size;
+ GstVideoFormat format = GST_VIDEO_INFO_FORMAT (&self->vinfo);
+ guint32 drm_fourcc = gst_video_format_to_wl_dmabuf_format (format);
+
+ if (!self->allocator)
+ return GST_BUFFER_POOL_CLASS (parent_class)->alloc_buffer (pool, buffer,
+ params);
+
+ drm_mem = gst_drm_dumb_allocator_alloc (self->allocator,
+ drm_fourcc, GST_VIDEO_INFO_WIDTH (&self->vinfo),
+ GST_VIDEO_INFO_HEIGHT (&self->vinfo), &pitch);
+ if (!drm_mem)
+ goto alloc_failed;
+
+ dma_mem = gst_drm_dumb_memory_export_dmabuf (drm_mem);
+
+ gst_memory_unref (drm_mem);
+ drm_mem = NULL;
+
+ gst_wl_update_video_info_from_pitch (&self->vinfo,
+ GST_VIDEO_INFO_N_PLANES (&self->vinfo), pitch);
+
+ gst_memory_get_sizes (dma_mem, NULL, &dumbbuf_size);
+ if (dumbbuf_size < GST_VIDEO_INFO_SIZE (&self->vinfo)) {
+ GST_ERROR_OBJECT (self,
+ "DUMB buffer has a size of %" G_GSIZE_FORMAT
+ " but we require at least %" G_GSIZE_FORMAT " to hold a frame",
+ dumbbuf_size, GST_VIDEO_INFO_SIZE (&self->vinfo));
+ goto buffer_too_small;
+ }
+
+ gst_memory_resize (dma_mem, 0, GST_VIDEO_INFO_SIZE (&self->vinfo));
+
+ buf = gst_buffer_new ();
+ gst_buffer_append_memory (buf, dma_mem);
+ gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
+ GST_VIDEO_INFO_FORMAT (&self->vinfo), GST_VIDEO_INFO_WIDTH (&self->vinfo),
+ GST_VIDEO_INFO_HEIGHT (&self->vinfo),
+ GST_VIDEO_INFO_N_PLANES (&self->vinfo),
+ self->vinfo.offset, self->vinfo.stride);
+
+ *buffer = buf;
+
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+alloc_failed:
+ {
+ GST_ERROR_OBJECT (self, "failed to allocate DRM Dumb buffer.");
+ return GST_FLOW_ERROR;
+ }
+buffer_too_small:
+ {
+ GST_ERROR_OBJECT (self, "dumb buffer too small to store an image.");
+ return GST_FLOW_ERROR;
+ }
+}
static const gchar **
gst_wl_video_buffer_pool_get_options (GstBufferPool * pool)
@@ -39,11 +229,15 @@ gst_wl_video_buffer_pool_class_init (GstWlVideoBufferPoolClass * klass)
{
GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass);
pool_class->get_options = gst_wl_video_buffer_pool_get_options;
+ pool_class->start = gst_wl_video_buffer_pool_start;
+ pool_class->stop = gst_wl_video_buffer_pool_stop;
+ pool_class->alloc_buffer = gst_wl_video_buffer_pool_alloc_buffer;
}
static void
-gst_wl_video_buffer_pool_init (GstWlVideoBufferPool * pool)
+gst_wl_video_buffer_pool_init (GstWlVideoBufferPool * self)
{
+ gst_video_info_init (&self->vinfo);
}
GstBufferPool *
diff --git a/gst-libs/gst/wayland/gstwlvideobufferpool.h b/gst-libs/gst/wayland/gstwlvideobufferpool.h
index 297afbc..fe04186 100644
--- a/gst-libs/gst/wayland/gstwlvideobufferpool.h
+++ b/gst-libs/gst/wayland/gstwlvideobufferpool.h
@@ -26,20 +26,16 @@
G_BEGIN_DECLS
-/* A tiny GstVideoBufferPool subclass that modify the options to remove
+/* A GstVideoBufferPool subclass that modify the options to remove
* VideoAlignment. To support VideoAlignment we would need to pass the padded
* width/height + stride and use the viewporter interface to crop, a bit like
* we use to do with XV. It would still be quite limited. It's a bit retro,
- * hopefully there will be a better Wayland interface in the future. */
+ * hopefully there will be a better Wayland interface in the future. This buffer
+ * pool also support GstDRMDumbAllocator. */
#define GST_TYPE_WL_VIDEO_BUFFER_POOL (gst_wl_video_buffer_pool_get_type ())
G_DECLARE_FINAL_TYPE (GstWlVideoBufferPool, gst_wl_video_buffer_pool, GST, WL_VIDEO_BUFFER_POOL, GstVideoBufferPool);
-struct _GstWlVideoBufferPool
-{
- GstVideoBufferPool parent;
-};
-
GST_WL_API
GstBufferPool * gst_wl_video_buffer_pool_new (void);
--
2.25.1

View File

@@ -0,0 +1,411 @@
From d5807fc0840117a8472922d69df76ddebf633325 Mon Sep 17 00:00:00 2001
From: Colin Kinloch <colin.kinloch@collabora.com>
Date: Thu, 16 Feb 2023 16:21:33 -0500
Subject: [PATCH 11/17] wayladnsink: Add DRM Dumb allocator support
If the input is not a DMABuf, attempt to copy into a DRM Dumb
buffer and import it has a DMABuf. This will offload the
compositor from actually doing this copy (needed to handle SHM)
and may allow the software decoded stream to be rendered to
an HW layer, or even reach through some better accelerated
GL import path.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
ext/gtk/gstgtkwaylandsink.c | 121 ++++++++++++++++++++++++++++++++++-
ext/wayland/gstwaylandsink.c | 110 +++++++++++++++++++++++++++++++
ext/wayland/gstwaylandsink.h | 3 +
3 files changed, 233 insertions(+), 1 deletion(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index c46207c..438294b 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -87,7 +87,8 @@ enum
PROP_0,
PROP_WIDGET,
PROP_DISPLAY,
- PROP_ROTATE_METHOD
+ PROP_ROTATE_METHOD,
+ PROP_DRM_DEVICE,
};
typedef struct _GstGtkWaylandSinkPrivate
@@ -119,6 +120,9 @@ typedef struct _GstGtkWaylandSinkPrivate
GstVideoOrientationMethod current_rotate_method;
struct wl_callback *callback;
+
+ gchar *drm_device;
+ gboolean skip_dumb_buffer_copy;
} GstGtkWaylandSinkPrivate;
#define gst_gtk_wayland_sink_parent_class parent_class
@@ -161,6 +165,17 @@ gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass)
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstGtkWaylandSink:drm-device:
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
+ g_param_spec_string ("drm-device", "DRM Device", "Path of the "
+ "DRM device to use for dumb buffer allocation",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_gtk_wayland_sink_change_state);
@@ -207,6 +222,8 @@ gst_gtk_wayland_sink_finalize (GObject * object)
g_clear_object (&priv->gtk_widget);
gst_clear_caps (&priv->caps);
+ g_free (priv->drm_device);
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -379,6 +396,11 @@ gst_gtk_wayland_sink_get_property (GObject * object, guint prop_id,
case PROP_ROTATE_METHOD:
g_value_set_enum (value, priv->current_rotate_method);
break;
+ case PROP_DRM_DEVICE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, priv->drm_device);
+ GST_OBJECT_UNLOCK (self);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -390,12 +412,19 @@ gst_gtk_wayland_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object);
+ GstGtkWaylandSinkPrivate *priv =
+ gst_gtk_wayland_sink_get_instance_private (self);
switch (prop_id) {
case PROP_ROTATE_METHOD:
gst_gtk_wayland_sink_set_rotate_method (self, g_value_get_enum (value),
FALSE);
break;
+ case PROP_DRM_DEVICE:
+ GST_OBJECT_LOCK (self);
+ priv->drm_device = g_value_dup_string (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -710,6 +739,8 @@ gst_gtk_wayland_sink_change_state (GstElement * element,
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
+ g_clear_object (&priv->pool);
+ /* fallthrough */
case GST_STATE_CHANGE_NULL_TO_NULL:
gst_gtk_invoke_on_main ((GThreadFunc)
gst_gtk_wayland_sink_stop_on_main, element);
@@ -860,6 +891,41 @@ gst_gtk_wayland_activate_shm_pool (GstGtkWaylandSink * self)
return TRUE;
}
+static gboolean
+gst_gtk_wayland_activate_drm_dumb_pool (GstGtkWaylandSink * self)
+{
+ GstGtkWaylandSinkPrivate *priv =
+ gst_gtk_wayland_sink_get_instance_private (self);
+ GstAllocator *alloc;
+
+ if (!priv->drm_device)
+ return FALSE;
+
+ if (priv->pool && gst_buffer_pool_is_active (priv->pool)) {
+ GstStructure *config = gst_buffer_pool_get_config (priv->pool);
+ gboolean ret = FALSE;
+ gboolean is_drm_dumb = FALSE;
+
+ ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL);
+ gst_structure_free (config);
+
+ if (ret && alloc)
+ is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc);
+
+ if (is_drm_dumb)
+ return TRUE;
+ }
+
+ alloc = gst_drm_dumb_allocator_new_with_device_path (priv->drm_device);
+ if (!alloc)
+ return FALSE;
+
+ gst_gtk_wayland_update_pool (self, alloc);
+ gst_object_unref (alloc);
+
+ return TRUE;
+}
+
static gboolean
gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
@@ -877,6 +943,7 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&priv->video_info);
priv->video_info_changed = TRUE;
+ priv->skip_dumb_buffer_copy = FALSE;
/* free pooled buffer used with previous caps */
if (priv->pool) {
@@ -1086,8 +1153,60 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display,
&priv->video_info);
+
+ /* DMABuf did not work, let try and make this a dmabuf, it does not matter
+ * if it was a SHM since the compositor needs to copy that anyway, and
+ * offloading the compositor from a copy helps maintaining a smoother
+ * desktop.
+ */
+ if (!priv->skip_dumb_buffer_copy) {
+ GstVideoFrame src, dst;
+
+ if (!gst_gtk_wayland_activate_drm_dumb_pool (self)) {
+ priv->skip_dumb_buffer_copy = TRUE;
+ goto handle_shm;
+ }
+
+ ret = gst_buffer_pool_acquire_buffer (priv->pool, &to_render, NULL);
+ if (ret != GST_FLOW_OK)
+ goto no_buffer;
+
+ wlbuffer = gst_buffer_get_wl_buffer (priv->display, to_render);
+
+ /* attach a wl_buffer if there isn't one yet */
+ if (G_UNLIKELY (!wlbuffer)) {
+ wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render,
+ priv->display, &priv->video_info);
+
+ if (G_UNLIKELY (!wbuf)) {
+ GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf");
+ gst_clear_buffer (&to_render);
+ priv->skip_dumb_buffer_copy = TRUE;
+ goto handle_shm;
+ }
+
+ wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, priv->display);
+ }
+
+ if (!gst_video_frame_map (&dst, &priv->video_info, to_render,
+ GST_MAP_WRITE))
+ goto dst_map_failed;
+
+ if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) {
+ gst_video_frame_unmap (&dst);
+ goto src_map_failed;
+ }
+
+ gst_video_frame_copy (&dst, &src);
+
+ gst_video_frame_unmap (&src);
+ gst_video_frame_unmap (&dst);
+
+ goto render;
+ }
}
+handle_shm:
if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index aa39447..bb7fcff 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -61,6 +61,7 @@ enum
PROP_DISPLAY,
PROP_FULLSCREEN,
PROP_ROTATE_METHOD,
+ PROP_DRM_DEVICE,
PROP_LAST
};
@@ -177,6 +178,18 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
GST_TYPE_VIDEO_ORIENTATION_METHOD, GST_VIDEO_ORIENTATION_IDENTITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * waylandsink:drm-device:
+ *
+ * Since: 1.24
+ */
+ g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
+ g_param_spec_string ("drm-device", "DRM Device", "Path of the "
+ "DRM device to use for dumb buffer allocation",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
+
/**
* waylandsink:render-rectangle:
*
@@ -266,6 +279,11 @@ gst_wayland_sink_get_property (GObject * object,
g_value_set_enum (value, self->current_rotate_method);
GST_OBJECT_UNLOCK (self);
break;
+ case PROP_DRM_DEVICE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, self->drm_device);
+ GST_OBJECT_UNLOCK (self);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -293,6 +311,11 @@ gst_wayland_sink_set_property (GObject * object,
gst_wayland_sink_set_rotate_method (self, g_value_get_enum (value),
FALSE);
break;
+ case PROP_DRM_DEVICE:
+ GST_OBJECT_LOCK (self);
+ self->drm_device = g_value_dup_string (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -318,6 +341,7 @@ gst_wayland_sink_finalize (GObject * object)
gst_clear_caps (&self->caps);
g_free (self->display_name);
+ g_free (self->drm_device);
g_mutex_clear (&self->display_lock);
g_mutex_clear (&self->render_lock);
@@ -629,6 +653,39 @@ gst_wayland_activate_shm_pool (GstWaylandSink * self)
return TRUE;
}
+static gboolean
+gst_wayland_activate_drm_dumb_pool (GstWaylandSink * self)
+{
+ GstAllocator *alloc;
+
+ if (!self->drm_device)
+ return FALSE;
+
+ if (self->pool && gst_buffer_pool_is_active (self->pool)) {
+ GstStructure *config = gst_buffer_pool_get_config (self->pool);
+ gboolean ret = FALSE;
+ gboolean is_drm_dumb = FALSE;
+
+ ret = gst_buffer_pool_config_get_allocator (config, &alloc, NULL);
+ gst_structure_free (config);
+
+ if (ret && alloc)
+ is_drm_dumb = GST_IS_DRM_DUMB_ALLOCATOR (alloc);
+
+ if (is_drm_dumb)
+ return TRUE;
+ }
+
+ alloc = gst_drm_dumb_allocator_new_with_device_path (self->drm_device);
+ if (!alloc)
+ return FALSE;
+
+ gst_wayland_update_pool (self, alloc);
+ gst_object_unref (alloc);
+
+ return TRUE;
+}
+
static gboolean
gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
@@ -644,6 +701,7 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
format = GST_VIDEO_INFO_FORMAT (&self->video_info);
self->video_info_changed = TRUE;
+ self->skip_dumb_buffer_copy = FALSE;
/* free pooled buffer used with previous caps */
if (self->pool) {
@@ -848,8 +906,60 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer)))
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display,
&self->video_info);
+
+ /* DMABuf did not work, let try and make this a dmabuf, it does not matter
+ * if it was a SHM since the compositor needs to copy that anyway, and
+ * offloading the compositor from a copy helps maintaining a smoother
+ * desktop.
+ */
+ if (!self->skip_dumb_buffer_copy) {
+ GstVideoFrame src, dst;
+
+ if (!gst_wayland_activate_drm_dumb_pool (self)) {
+ self->skip_dumb_buffer_copy = TRUE;
+ goto handle_shm;
+ }
+
+ ret = gst_buffer_pool_acquire_buffer (self->pool, &to_render, NULL);
+ if (ret != GST_FLOW_OK)
+ goto no_buffer;
+
+ wlbuffer = gst_buffer_get_wl_buffer (self->display, to_render);
+
+ /* attach a wl_buffer if there isn't one yet */
+ if (G_UNLIKELY (!wlbuffer)) {
+ wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (to_render,
+ self->display, &self->video_info);
+
+ if (G_UNLIKELY (!wbuf)) {
+ GST_WARNING_OBJECT (self, "failed to import DRM Dumb dmabuf");
+ gst_clear_buffer (&to_render);
+ self->skip_dumb_buffer_copy = TRUE;
+ goto handle_shm;
+ }
+
+ wlbuffer = gst_buffer_add_wl_buffer (to_render, wbuf, self->display);
+ }
+
+ if (!gst_video_frame_map (&dst, &self->video_info, to_render,
+ GST_MAP_WRITE))
+ goto dst_map_failed;
+
+ if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) {
+ gst_video_frame_unmap (&dst);
+ goto src_map_failed;
+ }
+
+ gst_video_frame_copy (&dst, &src);
+
+ gst_video_frame_unmap (&src);
+ gst_video_frame_unmap (&dst);
+
+ goto render;
+ }
}
+handle_shm:
if (!wbuf && gst_wl_display_check_format_for_shm (self->display, format)) {
if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display,
diff --git a/ext/wayland/gstwaylandsink.h b/ext/wayland/gstwaylandsink.h
index 1ff50d1..ca3400e 100644
--- a/ext/wayland/gstwaylandsink.h
+++ b/ext/wayland/gstwaylandsink.h
@@ -69,6 +69,9 @@ struct _GstWaylandSink
GstVideoOrientationMethod current_rotate_method;
struct wl_callback *callback;
+
+ gchar *drm_device;
+ gboolean skip_dumb_buffer_copy;
};
struct _GstWaylandSinkClass
--
2.25.1

View File

@@ -0,0 +1,57 @@
From f0e7d7840db7a27d29127acdd42a09ea7450c969 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Fri, 17 Feb 2023 12:10:25 -0500
Subject: [PATCH 12/17] bad: Update doc cache for waylandsink changes
A new drm-device property has been added.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
docs/plugins/gst_plugins_cache.json | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json
index 50128fb..4ea6d90 100644
--- a/docs/plugins/gst_plugins_cache.json
+++ b/docs/plugins/gst_plugins_cache.json
@@ -29462,6 +29462,18 @@
}
},
"properties": {
+ "drm-device": {
+ "blurb": "Path of the DRM device to use for dumb buffer allocation",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": true,
+ "controllable": false,
+ "default": "NULL",
+ "mutable": "null",
+ "readable": true,
+ "type": "gchararray",
+ "writable": true
+ },
"rotate-method": {
"blurb": "rotate method",
"conditionally-available": false,
@@ -236521,6 +236533,18 @@
"type": "gchararray",
"writable": true
},
+ "drm-device": {
+ "blurb": "Path of the DRM device to use for dumb buffer allocation",
+ "conditionally-available": false,
+ "construct": false,
+ "construct-only": true,
+ "controllable": false,
+ "default": "NULL",
+ "mutable": "null",
+ "readable": true,
+ "type": "gchararray",
+ "writable": true
+ },
"fullscreen": {
"blurb": "Whether the surface should be made fullscreen ",
"conditionally-available": false,
--
2.25.1

View File

@@ -0,0 +1,26 @@
From 0bc64942a8f0c1b49e2488b52f083c5545b1079c Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 13:11:55 +0200
Subject: [PATCH 14/17] WAYLANDSINK: use card0a as default drm-device
Signed-off-by: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
---
ext/wayland/gstwaylandsink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index bb7fcff..8cf8e36 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -186,7 +186,7 @@ gst_wayland_sink_class_init (GstWaylandSinkClass * klass)
g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
g_param_spec_string ("drm-device", "DRM Device", "Path of the "
"DRM device to use for dumb buffer allocation",
- NULL,
+ "/dev/dri/card0",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
--
2.25.1

View File

@@ -0,0 +1,26 @@
From a4873e027038d26e9fb43a03a016d2975e6b3ba3 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 13:14:01 +0200
Subject: [PATCH 15/17] waylandsink: Uprank to secondary
Signed-off-by: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
---
ext/wayland/gstwaylandsink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 8cf8e36..24a7174 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -114,7 +114,7 @@ static void gst_wayland_sink_expose (GstVideoOverlay * overlay);
G_DEFINE_TYPE_WITH_CODE (GstWaylandSink, gst_wayland_sink, GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
gst_wayland_sink_videooverlay_init));
-GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_MARGINAL,
+GST_ELEMENT_REGISTER_DEFINE (waylandsink, "waylandsink", GST_RANK_SECONDARY+1,
GST_TYPE_WAYLAND_SINK);
static void
--
2.25.1

View File

@@ -0,0 +1,25 @@
From a028c20bb3ad4f17b60afacb2a5f2a624432b2d4 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 14:42:17 +0200
Subject: [PATCH 16/17] gstwlshmallocator: correct WL API declaration
Signed-off-by: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
---
gst-libs/gst/wayland/gstwlshmallocator.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/gst-libs/gst/wayland/gstwlshmallocator.h b/gst-libs/gst/wayland/gstwlshmallocator.h
index 0e7e15d..dcbea6c 100644
--- a/gst-libs/gst/wayland/gstwlshmallocator.h
+++ b/gst-libs/gst/wayland/gstwlshmallocator.h
@@ -30,6 +30,7 @@
G_BEGIN_DECLS
#define GST_TYPE_WL_SHM_ALLOCATOR (gst_wl_shm_allocator_get_type ())
+GST_WL_API
G_DECLARE_FINAL_TYPE (GstWlShmAllocator, gst_wl_shm_allocator, GST, WL_SHM_ALLOCATOR, GstFdAllocator);
#define GST_ALLOCATOR_WL_SHM "wl_shm"
--
2.25.1

View File

@@ -0,0 +1,28 @@
From 31d07ae72c3b78936bd4a385f1f5a72a2171da8b Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 14:46:04 +0200
Subject: [PATCH 17/17] sts/3801>gtkwaylandsink: Destroy GstWlWindow when
parent GtkWindow is destroyed
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4197>
---
ext/gtk/gstgtkwaylandsink.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index e32bbcc..9691255 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -961,6 +961,9 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
if (!gst_wl_display_check_format_for_dmabuf (priv->display, format))
goto unsupported_format;
} else if (!gst_wl_display_check_format_for_shm (priv->display, format)) {
+ /* Note: we still support dmabuf in this case, but formats must also be
+ * supported on SHM interface to ensure a fallback is possible as we are
+ * not guarantied we'll get dmabuf in the buffers. */
goto unsupported_format;
}
--
2.25.1

View File

@@ -0,0 +1,25 @@
From 11f6e311f37ca4aaf5bafb9bc646686905c5c377 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Wed, 29 Mar 2023 18:28:39 +0200
Subject: [PATCH] GTKWAYLANDSINK: use card0 as default drm-device
---
ext/gtk/gstgtkwaylandsink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 9691255..985ac07 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -173,7 +173,7 @@ gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass)
g_object_class_install_property (gobject_class, PROP_DRM_DEVICE,
g_param_spec_string ("drm-device", "DRM Device", "Path of the "
"DRM device to use for dumb buffer allocation",
- NULL,
+ "/dev/dri/card0",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
gstelement_class->change_state =
--
2.25.1

View File

@@ -0,0 +1,87 @@
From 5c44d0f7282ae768aa6c46ee63ea924847035963 Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@foss.st.com>
Date: Tue, 30 May 2023 14:08:58 +0200
Subject: [PATCH 19/19] waylandsink: Emit "map" signal boarder surface is ready
This allows gtkwaylandsink to queue a draw of its gtk widget at the
correct time, avoiding a race.
Signed-off-by: Colin Kinloch <colin.kinloch@collabora.com>
---
ext/gtk/gstgtkwaylandsink.c | 13 +++++++++++++
gst-libs/gst/wayland/gstwlwindow.c | 7 ++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 985ac07..2debee6 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -462,6 +462,17 @@ scrollable_window_adjustment_changed_cb (GtkAdjustment * adjustment,
return FALSE;
}
+static void
+wl_window_map_cb (GstWlWindow * wl_window, GstGtkWaylandSink * self)
+{
+ GstGtkWaylandSinkPrivate *priv =
+ gst_gtk_wayland_sink_get_instance_private (self);
+
+ GST_DEBUG_OBJECT (self, "waylandsink surface is ready");
+
+ gtk_gst_base_widget_queue_draw (GTK_GST_BASE_WIDGET (priv->gtk_widget));
+}
+
static void
setup_wl_window (GstGtkWaylandSink * self)
{
@@ -488,6 +499,8 @@ setup_wl_window (GstGtkWaylandSink * self)
wl_surface, &priv->render_lock);
gst_wl_window_set_rotate_method (priv->wl_window,
priv->current_rotate_method);
+ g_signal_connect_object (priv->wl_window, "map",
+ G_CALLBACK (wl_window_map_cb), self, 0);
}
/* In order to position the subsurface correctly within a scrollable widget,
diff --git a/gst-libs/gst/wayland/gstwlwindow.c b/gst-libs/gst/wayland/gstwlwindow.c
index 4aa53c3..afdbc10 100644
--- a/gst-libs/gst/wayland/gstwlwindow.c
+++ b/gst-libs/gst/wayland/gstwlwindow.c
@@ -79,6 +79,7 @@ G_DEFINE_TYPE_WITH_CODE (GstWlWindow, gst_wl_window, G_TYPE_OBJECT,
enum
{
CLOSED,
+ MAP,
LAST_SIGNAL
};
@@ -155,6 +156,9 @@ gst_wl_window_class_init (GstWlWindowClass * klass)
signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+ signals[MAP] = g_signal_new ("map", G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
}
static void
@@ -344,7 +348,7 @@ error:
GstWlWindow *
gst_wl_window_new_in_surface (GstWlDisplay * display,
- struct wl_surface * parent, GMutex * render_lock)
+ struct wl_surface *parent, GMutex * render_lock)
{
GstWlWindow *self;
GstWlWindowPrivate *priv;
@@ -509,6 +513,7 @@ gst_wl_window_render (GstWlWindow * self, GstWlBuffer * buffer,
gst_wl_window_update_borders (self);
wl_surface_commit (priv->area_surface_wrapper);
priv->is_area_surface_mapped = TRUE;
+ g_signal_emit (self, signals[MAP], 0);
}
} else {
/* clear both video and parent surfaces */
--
2.25.1

View File

@@ -0,0 +1,65 @@
From f7b4ff8313d4e2197bdb91d21cd0a7beb59b7b7c Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Wed, 14 Jun 2023 11:24:49 +0200
Subject: [PATCH 20/21] gtkwaylandsink: do not use drm dumb pool with DMAbuf
buffers
There is no need to use our DRM dumb pool if buffer to
render is already a DMABuf, just import it and render it.
This fixes a DMAbuf memory leakage identfied with command:
watch "cat /sys/kernel/debug/dma_buf/bufinfo | grep attached "
Signed-off-by: Hugues Fruchet <hugues.fruchet@foss.st.com>
---
ext/gtk/gstgtkwaylandsink.c | 6 +++++-
ext/wayland/gstwaylandsink.c | 6 +++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 2debee6..7537200 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -1175,12 +1175,16 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display,
&priv->video_info);
+ if (wbuf)
+ GST_LOG_OBJECT (self,
+ "buffer %" GST_PTR_FORMAT " is DMABuf", buffer);
+
/* DMABuf did not work, let try and make this a dmabuf, it does not matter
* if it was a SHM since the compositor needs to copy that anyway, and
* offloading the compositor from a copy helps maintaining a smoother
* desktop.
*/
- if (!priv->skip_dumb_buffer_copy) {
+ if (!wbuf && !priv->skip_dumb_buffer_copy) {
GstVideoFrame src, dst;
if (!gst_gtk_wayland_activate_drm_dumb_pool (self)) {
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 24a7174..1662241 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -907,12 +907,16 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display,
&self->video_info);
+ if (wbuf)
+ GST_LOG_OBJECT (self,
+ "buffer %" GST_PTR_FORMAT " is DMABuf", buffer);
+
/* DMABuf did not work, let try and make this a dmabuf, it does not matter
* if it was a SHM since the compositor needs to copy that anyway, and
* offloading the compositor from a copy helps maintaining a smoother
* desktop.
*/
- if (!self->skip_dumb_buffer_copy) {
+ if (!wbuf && !self->skip_dumb_buffer_copy) {
GstVideoFrame src, dst;
if (!gst_wayland_activate_drm_dumb_pool (self)) {
--
2.25.1

View File

@@ -0,0 +1,650 @@
From ebb197aacf8cb21a79704851b045d28592848953 Mon Sep 17 00:00:00 2001
From: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Date: Mon, 16 Jan 2023 17:34:10 +0100
Subject: [PATCH 1/5] codecs: Add base class for stateless vp8 encoder
Add stateless vp8 encoder base class and vp8 frame structure.
---
gst-libs/gst/codecs/gstvp8encoder.c | 351 ++++++++++++++++++++++++++++
gst-libs/gst/codecs/gstvp8encoder.h | 81 +++++++
gst-libs/gst/codecs/gstvp8frame.c | 66 ++++++
gst-libs/gst/codecs/gstvp8frame.h | 70 ++++++
gst-libs/gst/codecs/meson.build | 7 +-
5 files changed, 574 insertions(+), 1 deletion(-)
create mode 100644 gst-libs/gst/codecs/gstvp8encoder.c
create mode 100644 gst-libs/gst/codecs/gstvp8encoder.h
create mode 100644 gst-libs/gst/codecs/gstvp8frame.c
create mode 100644 gst-libs/gst/codecs/gstvp8frame.h
diff --git a/gst-libs/gst/codecs/gstvp8encoder.c b/gst-libs/gst/codecs/gstvp8encoder.c
new file mode 100644
index 0000000..b105786
--- /dev/null
+++ b/gst-libs/gst/codecs/gstvp8encoder.c
@@ -0,0 +1,351 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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 "gstvp8encoder.h"
+
+#include <gst/video/video.h>
+#include <gst/video/gstvideometa.h>
+#include <gst/base/base.h>
+
+GST_DEBUG_CATEGORY (gst_vp8_encoder_debug);
+#define GST_CAT_DEFAULT gst_vp8_encoder_debug
+
+#define VP8ENC_DEFAULT_KEYFRAME_INTERVAL 30
+
+#define VP8_MAX_QUALITY 63
+#define VP8_MIN_QUALITY 0
+
+#define VP8_DEFAULT_BITRATE 100000
+
+enum
+{
+ PROP_0,
+ PROP_KEYFRAME_INTERVAL,
+ PROP_MAX_QUALITY,
+ PROP_MIN_QUALITY,
+ PROP_BITRATE,
+};
+
+struct _GstVp8EncoderPrivate
+{
+ gint keyframe_interval;
+
+ guint32 last_keyframe;
+
+ guint64 targeted_bitrate;
+ gint max_quality;
+ gint min_quality;
+ gint current_quality;
+ guint64 used_bytes;
+ guint64 nb_frames;
+};
+
+#define parent_class gst_vp8_encoder_parent_class
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVp8Encoder, gst_vp8_encoder,
+ GST_TYPE_VIDEO_ENCODER,
+ G_ADD_PRIVATE (GstVp8Encoder);
+ GST_DEBUG_CATEGORY_INIT (gst_vp8_encoder_debug, "vp8encoder", 0,
+ "Vp8 Video Encoder"));
+
+static void
+gst_vp8_encoder_init (GstVp8Encoder * self)
+{
+ self->priv = gst_vp8_encoder_get_instance_private (self);
+}
+
+static void
+gst_vp8_encoder_finalize (GObject * object)
+{
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_vp8_encoder_start (GstVideoEncoder * encoder)
+{
+ GstVp8Encoder *self = GST_VP8_ENCODER (encoder);
+ GstVp8EncoderPrivate *priv = self->priv;
+
+ priv->last_keyframe = 0;
+ priv->current_quality = priv->min_quality;
+ priv->used_bytes = 0;
+ priv->nb_frames = 0;
+
+ return TRUE;
+}
+
+static gboolean
+gst_vp8_encoder_stop (GstVideoEncoder * encoder)
+{
+ return TRUE;
+}
+
+static gboolean
+gst_vp8_encoder_set_format (GstVideoEncoder * encoder,
+ GstVideoCodecState * state)
+{
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_vp8_encoder_set_quality (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
+{
+ GstVp8EncoderPrivate *priv = self->priv;
+ GstVideoEncoder *encoder = GST_VIDEO_ENCODER (self);
+ GstVideoCodecState *output_state =
+ gst_video_encoder_get_output_state (encoder);
+ gint qp = priv->current_quality;
+ guint64 bitrate = 0;
+ guint fps_n = 30, fps_d = 1;
+
+ if (output_state == NULL)
+ return qp;
+
+ if (GST_VIDEO_INFO_FPS_N (&output_state->info) != 0) {
+ fps_n = GST_VIDEO_INFO_FPS_N (&output_state->info);
+ fps_d = GST_VIDEO_INFO_FPS_D (&output_state->info);
+ }
+ gst_video_codec_state_unref (output_state);
+
+ bitrate = (priv->used_bytes * 8 * fps_n) / (priv->nb_frames * fps_d);
+ if (bitrate > priv->targeted_bitrate) {
+ qp++;
+ }
+
+ if (bitrate < priv->targeted_bitrate) {
+ qp--;
+ }
+
+ if (qp > priv->max_quality)
+ qp = priv->max_quality;
+ if (qp < priv->min_quality)
+ qp = priv->min_quality;
+
+ vp8_frame->quality = qp;
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_vp8_encoder_set_frame_type (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
+{
+ GstVp8EncoderPrivate *priv = self->priv;
+ GstVideoCodecFrame *frame = vp8_frame->frame;
+
+ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
+ vp8_frame->type = GstVp8Keyframe;
+ return GST_FLOW_OK;
+ }
+
+ if ((frame->system_frame_number - priv->last_keyframe) >
+ priv->keyframe_interval || frame->system_frame_number == 0) {
+ /* Generate a keyframe */
+ GST_DEBUG_OBJECT (self, "Generate a keyframe");
+ vp8_frame->type = GstVp8Keyframe;
+ return GST_FLOW_OK;
+ }
+
+ /* Generate a interframe */
+ GST_DEBUG_OBJECT (self, "Generate a interframe");
+ vp8_frame->type = GstVp8Inter;
+ return GST_FLOW_OK;
+}
+
+static void
+gst_vp8_encoder_mark_frame (GstVp8Encoder * self, GstVp8Frame * vp8_frame)
+{
+ GstVideoCodecFrame *frame = vp8_frame->frame;
+ GstVp8EncoderPrivate *priv = self->priv;
+
+ switch (vp8_frame->type) {
+ case GstVp8Keyframe:
+ priv->last_keyframe = frame->system_frame_number;
+ break;
+ }
+
+ priv->current_quality = vp8_frame->quality;
+ priv->used_bytes += gst_buffer_get_size (frame->output_buffer);
+ priv->nb_frames++;
+}
+
+static GstFlowReturn
+gst_vp8_encoder_handle_frame (GstVideoEncoder * encoder,
+ GstVideoCodecFrame * frame)
+{
+ GstVp8Encoder *self = GST_VP8_ENCODER (encoder);
+ GstVp8EncoderClass *klass = GST_VP8_ENCODER_GET_CLASS (self);
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstVp8Frame *vp8_frame = gst_vp8_frame_new (frame);
+
+ ret = gst_vp8_encoder_set_frame_type (self, vp8_frame);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ ret = gst_vp8_encoder_set_quality (self, vp8_frame);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ /* TODO: add encoding parameters management here
+ * for now just send the frame to encode */
+ if (klass->encode_frame) {
+ ret = klass->encode_frame (self, vp8_frame);
+ if (ret == GST_FLOW_OK)
+ gst_vp8_encoder_mark_frame (self, vp8_frame);
+ }
+
+ gst_vp8_frame_unref (vp8_frame);
+
+ return ret;
+}
+
+static void
+gst_vp8_encoder_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstVp8Encoder *self = GST_VP8_ENCODER (object);
+ GstVp8EncoderPrivate *priv = self->priv;
+
+ switch (property_id) {
+ case PROP_KEYFRAME_INTERVAL:
+ GST_OBJECT_LOCK (self);
+ g_value_set_int (value, priv->keyframe_interval);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_MAX_QUALITY:
+ GST_OBJECT_LOCK (self);
+ g_value_set_int (value, priv->max_quality);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_MIN_QUALITY:
+ GST_OBJECT_LOCK (self);
+ g_value_set_int (value, priv->min_quality);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_BITRATE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint64 (value, priv->targeted_bitrate);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_vp8_encoder_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVp8Encoder *self = GST_VP8_ENCODER (object);
+ GstVp8EncoderPrivate *priv = self->priv;
+
+ switch (property_id) {
+ case PROP_KEYFRAME_INTERVAL:
+ GST_OBJECT_LOCK (self);
+ priv->keyframe_interval = g_value_get_int (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_MAX_QUALITY:
+ GST_OBJECT_LOCK (self);
+ priv->max_quality = g_value_get_int (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_MIN_QUALITY:
+ GST_OBJECT_LOCK (self);
+ priv->min_quality = g_value_get_int (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_BITRATE:
+ GST_OBJECT_LOCK (self);
+ priv->targeted_bitrate = g_value_get_uint64 (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_vp8_encoder_class_init (GstVp8EncoderClass * klass)
+{
+ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = GST_DEBUG_FUNCPTR (gst_vp8_encoder_finalize);
+ object_class->get_property = gst_vp8_encoder_get_property;
+ object_class->set_property = gst_vp8_encoder_set_property;
+
+ encoder_class->start = GST_DEBUG_FUNCPTR (gst_vp8_encoder_start);
+ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp8_encoder_stop);
+ encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp8_encoder_set_format);
+ encoder_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_vp8_encoder_handle_frame);
+
+ /**
+ * GstVp8Encoder:keyframe-interval:
+ *
+ *
+ * Since: 1.2x
+ */
+ g_object_class_install_property (object_class, PROP_KEYFRAME_INTERVAL,
+ g_param_spec_int ("keyframe-interval", "Keyframe Interval",
+ "Interval between keyframes",
+ 0, G_MAXINT, VP8ENC_DEFAULT_KEYFRAME_INTERVAL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+ /**
+ * GstVp8Encoder:max-quality:
+ *
+ *
+ * Since: 1.2x
+ */
+ g_object_class_install_property (object_class, PROP_MAX_QUALITY,
+ g_param_spec_int ("max-quality", "Max Quality Level",
+ "Set upper quality limit (lower number equates to higher quality but more bits)",
+ VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MAX_QUALITY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+ /**
+ * GstVp8Encoder:min-quality:
+ *
+ *
+ * Since: 1.2x
+ */
+ g_object_class_install_property (object_class, PROP_MIN_QUALITY,
+ g_param_spec_int ("min-quality", "Min Quality Level",
+ "Set lower quality limit (lower number equates to higher quality but more bits)",
+ VP8_MIN_QUALITY, VP8_MAX_QUALITY, VP8_MIN_QUALITY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+ /**
+ * GstVp8Encoder:bitrate:
+ *
+ *
+ * Since: 1.2x
+ */
+ g_object_class_install_property (object_class, PROP_BITRATE,
+ g_param_spec_uint64 ("bitrate", "Targeted bitrate",
+ "Set bitrate target",
+ 0, UINT_MAX, VP8_DEFAULT_BITRATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+}
diff --git a/gst-libs/gst/codecs/gstvp8encoder.h b/gst-libs/gst/codecs/gstvp8encoder.h
new file mode 100644
index 0000000..3999ed3
--- /dev/null
+++ b/gst-libs/gst/codecs/gstvp8encoder.h
@@ -0,0 +1,81 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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_VP8_ENCODER_H__
+#define __GST_VP8_ENCODER_H__
+
+#include <gst/codecs/codecs-prelude.h>
+
+#include <gst/video/video.h>
+#include <gst/video/gstvideoencoder.h>
+
+#include "gstvp8frame.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_VP8_ENCODER (gst_vp8_encoder_get_type())
+#define GST_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VP8_ENCODER,GstVp8Encoder))
+#define GST_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass))
+#define GST_VP8_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VP8_ENCODER,GstVp8EncoderClass))
+#define GST_IS_VP8_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VP8_ENCODER))
+#define GST_IS_VP8_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VP8_ENCODER))
+#define GST_VP8_ENCODER_CAST(obj) ((GstVp8Encoder*)obj)
+typedef struct _GstVp8Encoder GstVp8Encoder;
+typedef struct _GstVp8EncoderClass GstVp8EncoderClass;
+typedef struct _GstVp8EncoderPrivate GstVp8EncoderPrivate;
+
+/**
+ * GstVp8Encoder:
+ *
+ * The opaque #GstVp8Encoder data structure.
+ */
+struct _GstVp8Encoder
+{
+ /*< private > */
+ GstVideoEncoder parent;
+
+ /*< private > */
+ GstVp8EncoderPrivate *priv;
+ gpointer padding[GST_PADDING_LARGE];
+};
+
+/**
+ * GstVp8EncoderClass:
+ */
+struct _GstVp8EncoderClass
+{
+ GstVideoEncoderClass parent_class;
+
+ /**
+ * GstVp8EncoderClass::encode_frame:
+ * @encoder: a #GstVp8Encoder
+ * @frame: a #GstVp8Frame
+ *
+ * Provide the frame to be encoded with the encode parameters (to be defined)
+ */
+ GstFlowReturn (*encode_frame) (GstVp8Encoder * encoder,
+ GstVp8Frame * frame);
+ /*< private > */
+ gpointer padding[GST_PADDING_LARGE];
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstVp8Encoder, gst_object_unref)
+ GST_CODECS_API GType gst_vp8_encoder_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_VP8_ENCODER_H__ */
diff --git a/gst-libs/gst/codecs/gstvp8frame.c b/gst-libs/gst/codecs/gstvp8frame.c
new file mode 100644
index 0000000..6f26bd5
--- /dev/null
+++ b/gst-libs/gst/codecs/gstvp8frame.c
@@ -0,0 +1,66 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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 "gstvp8frame.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_vp8_encoder_debug);
+#define GST_CAT_DEFAULT gst_vp8_encoder_debug
+
+GST_DEFINE_MINI_OBJECT_TYPE (GstVp8Frame, gst_vp8_frame);
+
+static void
+_gst_vp8_frame_free (GstVp8Frame * frame)
+{
+ GST_TRACE ("Free frame %p", frame);
+
+ gst_video_codec_frame_unref (frame->frame);
+
+ g_free (frame);
+}
+
+/**
+ * gst_vp8_frame_new:
+ *
+ * Create new #GstVp8Frame
+ *
+ * Returns: a new #GstVp8Frame
+ */
+GstVp8Frame *
+gst_vp8_frame_new (GstVideoCodecFrame * f)
+{
+ GstVp8Frame *frame;
+
+ if (!f)
+ return NULL;
+
+ frame = g_new0 (GstVp8Frame, 1);
+
+ gst_mini_object_init (GST_MINI_OBJECT_CAST (frame), 0,
+ GST_TYPE_VP8_FRAME, NULL, NULL,
+ (GstMiniObjectFreeFunction) _gst_vp8_frame_free);
+
+ frame->frame = gst_video_codec_frame_ref (f);
+
+ GST_TRACE ("New frame %p", frame);
+
+ return frame;
+}
diff --git a/gst-libs/gst/codecs/gstvp8frame.h b/gst-libs/gst/codecs/gstvp8frame.h
new file mode 100644
index 0000000..a3d5c21
--- /dev/null
+++ b/gst-libs/gst/codecs/gstvp8frame.h
@@ -0,0 +1,70 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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 __GTS_VP8_FRAME_H__
+#define __GTS_VP8_FRAME_H__
+
+#include <gst/codecs/codecs-prelude.h>
+#include <gst/video/video.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VP8_FRAME (gst_vp8_frame_get_type())
+#define GST_IS_VP8_FRAME(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_VP8_FRAME))
+#define GST_VP8_FRAME(obj) ((GstVp8Frame *)obj)
+#define GST_VP8_FRAME_CAST(obj) (GST_VP8_FRAME(obj))
+
+typedef struct _GstVp8Frame GstVp8Frame;
+
+enum
+{
+ GstVp8Keyframe,
+ GstVp8Inter,
+};
+
+struct _GstVp8Frame
+{
+ GstMiniObject parent;
+ gint type;
+ gint quality;
+
+ GstVideoCodecFrame *frame;
+};
+
+GST_CODECS_API
+GType gst_vp8_frame_get_type (void);
+
+GST_CODECS_API
+GstVp8Frame * gst_vp8_frame_new (GstVideoCodecFrame *f);
+
+static inline GstVp8Frame *
+gst_vp8_frame_ref (GstVp8Frame * frame)
+{
+ return (GstVp8Frame *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (frame));
+}
+
+static inline void
+gst_vp8_frame_unref (GstVp8Frame * frame)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (frame));
+}
+
+G_END_DECLS
+
+#endif /* __GTS_VP8_FRAME_H__ */
diff --git a/gst-libs/gst/codecs/meson.build b/gst-libs/gst/codecs/meson.build
index f2bec85..458d90d 100644
--- a/gst-libs/gst/codecs/meson.build
+++ b/gst-libs/gst/codecs/meson.build
@@ -12,6 +12,8 @@ codecs_sources = files(
'gstav1decoder.c',
'gstav1picture.c',
'gstvp9statefulparser.c',
+ 'gstvp8encoder.c',
+ 'gstvp8frame.c',
)
codecs_headers = files(
@@ -28,6 +30,8 @@ codecs_headers = files(
'gstav1decoder.h',
'gstav1picture.h',
'gstvp9statefulparser.h',
+ 'gstvp8encoder.h',
+ 'gstvp8frame.h',
)
cp_args = [
@@ -66,6 +70,7 @@ if build_gir
'--c-include=gst/codecs/gstvp9decoder.h',
'--c-include=gst/codecs/gstvp8decoder.h',
'--c-include=gst/codecs/gstmpeg2decoder.h',
+ '--c-include=gst/codecs/gstvp8encoder.h',
],
'dependencies' : [gstvideo_dep, gstcodecparsers_dep]
}
@@ -81,4 +86,4 @@ gstcodecs_dep = declare_dependency(link_with : gstcodecs,
include_directories : [libsinc],
sources: gen_sources,
dependencies : [gstvideo_dep, gstcodecparsers_dep])
-meson.override_dependency(pkg_name, gstcodecs_dep)
\ No newline at end of file
+meson.override_dependency(pkg_name, gstcodecs_dep)
--
2.25.1

View File

@@ -0,0 +1,130 @@
From 3addb9d9cc00a6f4bba6d57cee26ce8e47ee1f3d Mon Sep 17 00:00:00 2001
From: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Date: Mon, 16 Jan 2023 17:27:35 +0100
Subject: [PATCH 2/5] v4l2codecs: Add V4L2 VP8 stateless encode uAPI
Udpate v4l2 header files with the definitions and structures
used for stateless VP8 encode.
---
sys/v4l2codecs/linux/v4l2-controls.h | 91 ++++++++++++++++++++++++++++
sys/v4l2codecs/linux/videodev2.h | 2 +
2 files changed, 93 insertions(+)
diff --git a/sys/v4l2codecs/linux/v4l2-controls.h b/sys/v4l2codecs/linux/v4l2-controls.h
index 79625ee..8838a5d 100644
--- a/sys/v4l2codecs/linux/v4l2-controls.h
+++ b/sys/v4l2codecs/linux/v4l2-controls.h
@@ -2771,6 +2771,97 @@ struct v4l2_ctrl_vp9_compressed_hdr {
struct v4l2_vp9_mv_probs mv;
};
+#define V4L2_CID_STATELESS_VP8_ENCODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 501)
+
+#define V4L2_VP8_FRAME_FLAG_SHOWFRAME 0x1
+#define V4L2_VP8_FRAME_FLAG_GOLDEN_REFRESH 0x2
+#define V4L2_VP8_FRAME_FLAG_ALTREF_REFRESH 0x4
+#define V4L2_VP8_FRAME_FLAG_SEGMENT_ENABLED 0x8
+#define V4L2_VP8_FRAME_FLAG_LOOP_FILTER_ADJ_ENABLED 0x10
+#define V4L2_VP8_FRAME_FLAG_REFRESH_ENTROPY_PROBS 0x20
+
+#define V4L2_VP8_FRAME_TYPE_KEYFRAME 0
+#define V4L2_VP8_FRAME_TYPE_INTER 1
+
+#define V4L2_VP8_FRAME_COLOR_SPACE_YUV 0
+#define V4L2_VP8_FRAME_COLOR_SPACE_RESERVED 1
+
+#define V4L2_VP8_FRAME_CLAMPING_REQUIRED 0
+#define V4L2_VP8_FRAME_CLAMPING_NO 1
+
+#define V4L2_VP8_FRAME_FILTER_TYPE_NORMAL 0
+#define V4L2_VP8_FRAME_FILTER_TYPE_SIMPLE 1
+
+#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_1 0
+#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_2 1
+#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_4 2
+#define V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_8 3
+
+#define V4L2_VP8_FRAME_GOLDEN_KEEP 0
+#define V4L2_VP8_FRAME_GOLDEN_LASTFRAME 1
+#define V4L2_VP8_FRAME_GOLDEN_ALTREF 2
+
+#define V4L2_VP8_FRAME_ALTREF_KEEP 0
+#define V4L2_VP8_FRAME_ALTREF_LASTFRAME 1
+#define V4L2_VP8_FRAME_ALTREF_GOLDEN 2
+
+#define V4L2_VP8_FRAME_REF_LAST 0
+#define V4L2_VP8_FRAME_REF_GOLDEN 1
+#define V4L2_VP8_FRAME_REF_ALT 2
+
+/**
+ * struct v4l2_ctrl_vp8_encode_params - VP8 encode parameters
+ * @flags: combination of V4L2_VP8_FRAME_FLAG_{} flags.
+ * @frame_type: specifies the frame type (key or inter).
+ * Set to one of V4L2_VP8_FRAME_TYPE_{}.
+ * @color_space: defines the YUV color space of the sequence.
+ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero.
+ * Set to one of V4L2_VP8_FRAME_COLOR_SPACE_{}.
+ * @clamping_type: defines pixel value clamping type.
+ * V4L2_VP8_FRAME_TYPE_INTER frames shall set this field to zero.
+ * Set to one of V4L2_VP8_FRAME_CLAMPING_{}.
+ * @loop_filter_type: selects the type of loop filter applied.
+ * Set to one of V4L2_VP8_FRAME_FILTER_TYPE_{}.
+ * @loop_filter_level: sets the strength of the applied loop filter.
+ * Set to a value from the rage 0..63.
+ * @sharpness_level: sets the sharpness of the applied loop filter.
+ * Set to a value from the range 0..7.
+ * @log2_nbr_of_dct_partitions: determines the number of separate partitions
+ * containing the DCT coefficients of macroblocks.
+ * Set to one of V4L2_VP8_FRAME_NBR_DCT_PARTITIONS_{}.
+ * @prob_intra: indicates the probability of an intra macroblock.
+ * Set to a value from the range 0..255.
+ * @prob_last: indicates the probability that the last reference frame is used for inter-prediction.
+ * Set to a value from the range 0..255.
+ * @prob_gf: indicates the probability that the golden reference frame is used for inter-prediction.
+ * Set to a value from the range 0..255.
+ * @copy_buffer_to_golden: specifies the golden frame refresh strategy.
+ * Set to one of V4L2_VP8_FRAME_FLAG_GOLDEN_{}.
+ * @copy_buffer_to_alternate: specifies the atlref frame refresh strategy.
+ * Set to one of V4L2_VP8_FRAME_FLAG_ALTREF_{}.
+ * @reference_type: specifies what kind of reference to use for current inter frame.
+ * V4L2_VP8_FRAME_TYPE_KEYFRAME shall set this field to zero.
+ * Set to one of V4L2_VP8_FRAME_REF_{}.
+ */
+struct v4l2_ctrl_vp8_encode_params {
+ __u32 flags;
+ __u8 frame_type;
+ __u8 color_space;
+ __u8 clamping_type;
+ __u8 loop_filter_type;
+ __u8 loop_filter_level;
+ __u8 sharpness_level;
+ __u8 log2_nbr_of_dct_partitions;
+ __u8 prob_intra;
+ __u8 prob_last;
+ __u8 prob_gf;
+ __u8 copy_buffer_to_golden;
+ __u8 copy_buffer_to_alternate;
+ __u8 reference_type;
+};
+
+#define V4L2_CID_STATELESS_VP8_ENCODE_QP (V4L2_CID_CODEC_STATELESS_BASE + 502)
+
/* MPEG-compression definitions kept for backwards compatibility */
#define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC
#define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS
diff --git a/sys/v4l2codecs/linux/videodev2.h b/sys/v4l2codecs/linux/videodev2.h
index 62cc050..687ac85 100644
--- a/sys/v4l2codecs/linux/videodev2.h
+++ b/sys/v4l2codecs/linux/videodev2.h
@@ -1811,6 +1811,8 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS = 0x0272,
V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX = 0x0273,
V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS = 0x0274,
+
+ V4L2_CTRL_TYPE_VP8_ENCODE_PARAMS = 0x0280,
};
/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
--
2.25.1

View File

@@ -0,0 +1,812 @@
From 6b4f87ff52197510e6d4ca475ecdb5725473e60a Mon Sep 17 00:00:00 2001
From: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Date: Mon, 16 Jan 2023 17:38:58 +0100
Subject: [PATCH 4/5] v4l2codecs: Add V4L2 stateless VP8 encoder
Add element for V4L2 stateless VP8 encoder.
Using v4l2 request to set driver configuration by using VP8 stateless controls.
---
sys/v4l2codecs/gstv4l2codecvp8enc.c | 716 ++++++++++++++++++++++++++++
sys/v4l2codecs/gstv4l2codecvp8enc.h | 54 +++
sys/v4l2codecs/meson.build | 1 +
3 files changed, 771 insertions(+)
create mode 100644 sys/v4l2codecs/gstv4l2codecvp8enc.c
create mode 100644 sys/v4l2codecs/gstv4l2codecvp8enc.h
diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c
new file mode 100644
index 0000000..2f15caf
--- /dev/null
+++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c
@@ -0,0 +1,716 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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 "gstv4l2codecallocator.h"
+#include "gstv4l2codecpool.h"
+#include "gstv4l2codecvp8enc.h"
+#include "gstv4l2format.h"
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+#define V4L2_MIN_KERNEL_VER_MAJOR 5
+#define V4L2_MIN_KERNEL_VER_MINOR 17
+#define V4L2_MIN_KERNEL_VERSION KERNEL_VERSION(V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR, 0)
+
+GST_DEBUG_CATEGORY_STATIC (v4l2_vp8enc_debug);
+#define GST_CAT_DEFAULT v4l2_vp8enc_debug
+
+enum
+{
+ PROP_0,
+ PROP_LAST = PROP_0
+};
+
+static GstStaticPadTemplate sink_template =
+GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SINK_NAME,
+ GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_V4L2_DEFAULT_VIDEO_FORMATS)));
+
+static GstStaticPadTemplate src_template =
+GST_STATIC_PAD_TEMPLATE (GST_VIDEO_ENCODER_SRC_NAME,
+ GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-vp8"));
+
+struct _vp8_frame_hdr
+{
+ guint8 tag[3];
+ guint8 start_code[3];
+ guint16 width;
+ guint16 height;
+};
+
+#define VP8_FRAME_HDR_INTRA_FLAG 0x0
+#define VP8_FRAME_HDR_INTER_FLAG 0x1
+#define VP8_FRAME_HDR_VERSION1_FLAG (1 << 1)
+#define VP8_FRAME_HDR_SHOW_FRAME (1 << 4)
+#define VP8_FRAME_HDR_WIDTH_MASK 0x3fff
+#define VP8_FRAME_HDR_HEIGHT_MASK 0x3fff
+
+struct _GstV4l2CodecVp8Enc
+{
+ GstVp8Encoder parent;
+ GstV4l2Encoder *encoder;
+ GstVideoCodecState *output_state;
+ GstVideoInfo vinfo;
+ gint width;
+ gint height;
+ guint qp_max, qp_min;
+
+ GstV4l2CodecAllocator *sink_allocator;
+ GstV4l2CodecAllocator *src_allocator;
+ GstV4l2CodecPool *sink_pool;
+ GstV4l2CodecPool *src_pool;
+
+ struct v4l2_ctrl_vp8_encode_params encode_params;
+};
+
+G_DEFINE_ABSTRACT_TYPE (GstV4l2CodecVp8Enc, gst_v4l2_codec_vp8_enc,
+ GST_TYPE_VP8_ENCODER);
+
+#define parent_class gst_v4l2_codec_vp8_enc_parent_class
+
+static gboolean
+gst_v4l2_codec_vp8_enc_open (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+ guint version;
+
+ if (!gst_v4l2_encoder_open (self->encoder)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
+ ("Failed to open VP8 encoder"),
+ ("gst_v4l2_encoder_open() failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+
+ version = gst_v4l2_encoder_get_version (self->encoder);
+ if (version < V4L2_MIN_KERNEL_VERSION)
+ GST_WARNING_OBJECT (self,
+ "V4L2 API v%u.%u too old, at least v%u.%u required",
+ (version >> 16) & 0xff, (version >> 8) & 0xff,
+ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR);
+
+ GST_DEBUG_OBJECT (self, "open vp8 encoder");
+
+ return TRUE;
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_api_check (GstV4l2Encoder * encoder)
+{
+ guint i, ret_size;
+ /* *INDENT-OFF* */
+ #define SET_ID(cid) .id = (cid), .name = #cid
+ struct
+ {
+ const gchar *name;
+ unsigned int id;
+ unsigned int size;
+ gboolean optional;
+ } controls[] = {
+ {
+ SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_PARAMS),
+ .size = sizeof(struct v4l2_ctrl_vp8_encode_params),
+ }, {
+ SET_ID (V4L2_CID_STATELESS_VP8_ENCODE_QP),
+ .size = sizeof(__u32),
+ },
+ };
+ #undef SET_ID
+ /* *INDENT-ON* */
+
+ /*
+ * Compatibility check: make sure the pointer controls are
+ * the right size.
+ */
+ for (i = 0; i < G_N_ELEMENTS (controls); i++) {
+ gboolean control_found;
+
+ control_found = gst_v4l2_encoder_query_control_size (encoder,
+ controls[i].id, &ret_size);
+
+ if (!controls[i].optional && !control_found) {
+ GST_WARNING ("Driver is missing %s support.", controls[i].name);
+ return FALSE;
+ }
+
+ if (control_found && ret_size != controls[i].size) {
+ GST_WARNING ("%s control size mismatch: got %d bytes but %d expected.",
+ controls[i].name, ret_size, controls[i].size);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_close (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+ gst_v4l2_encoder_close (self->encoder);
+ return TRUE;
+}
+
+static void
+gst_v4l2_codec_vp8_enc_reset_allocation (GstV4l2CodecVp8Enc * self)
+{
+ if (self->sink_allocator) {
+ gst_v4l2_codec_allocator_detach (self->sink_allocator);
+ g_clear_object (&self->sink_allocator);
+ g_clear_object (&self->sink_pool);
+ }
+
+ if (self->src_allocator) {
+ gst_v4l2_codec_allocator_detach (self->src_allocator);
+ g_clear_object (&self->src_allocator);
+ g_clear_object (&self->src_pool);
+ }
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_start (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ GST_DEBUG_OBJECT (self, "start");
+
+ return GST_VIDEO_ENCODER_CLASS (parent_class)->start (encoder);
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_stop (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ GST_DEBUG_OBJECT (self, "stop");
+
+ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK);
+ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC);
+
+ gst_v4l2_codec_vp8_enc_reset_allocation (self);
+
+ if (self->output_state)
+ gst_video_codec_state_unref (self->output_state);
+ self->output_state = NULL;
+
+ return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (encoder);
+}
+
+static GstCaps *
+gst_v4l2_codec_vp8_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+ GstCaps *caps, *result;
+
+ caps = gst_v4l2_encoder_list_sink_formats (self->encoder);
+ GST_DEBUG_OBJECT (self, "Supported input formats: %" GST_PTR_FORMAT, caps);
+
+ result = gst_video_encoder_proxy_getcaps (encoder, caps, filter);
+
+ if (caps)
+ gst_caps_unref (caps);
+
+ GST_DEBUG_OBJECT (self, "Returning sink caps: %" GST_PTR_FORMAT, result);
+
+ return result;
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_buffers_allocation (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ GST_DEBUG_OBJECT (self, "buffers allocation");
+
+ g_clear_object (&self->sink_pool);
+ g_clear_object (&self->src_pool);
+ g_clear_object (&self->src_allocator);
+
+ self->sink_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder,
+ GST_PAD_SINK, 4);
+ if (!self->sink_allocator) {
+ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
+ ("Not enough memory to allocate sink buffers."), (NULL));
+ return FALSE;
+ }
+
+ self->sink_pool =
+ gst_v4l2_codec_pool_new (self->sink_allocator, &self->vinfo);
+
+ self->src_allocator = gst_v4l2_codec_encoder_allocator_new (self->encoder,
+ GST_PAD_SRC, 4);
+ if (!self->src_allocator) {
+ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
+ ("Not enough memory to allocate source buffers."), (NULL));
+ g_clear_object (&self->sink_allocator);
+ return FALSE;
+ }
+
+ self->src_pool = gst_v4l2_codec_pool_new (self->src_allocator, &self->vinfo);
+
+ return TRUE;
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_set_format (GstVideoEncoder * encoder,
+ GstVideoCodecState * state)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+ GstCaps *caps;
+
+ GST_DEBUG_OBJECT (self, "Set format");
+
+ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SINK);
+ gst_v4l2_encoder_streamoff (self->encoder, GST_PAD_SRC);
+
+ gst_v4l2_codec_vp8_enc_reset_allocation (self);
+
+ if (!gst_v4l2_encoder_select_sink_format (self->encoder, &state->info,
+ &self->vinfo)) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ ("Failed to configure VP8 encoder"),
+ ("gst_v4l2_encoder_select_sink_format() failed: %s",
+ g_strerror (errno)));
+ gst_v4l2_encoder_close (self->encoder);
+ return FALSE;
+ }
+
+ if (!gst_v4l2_encoder_set_src_fmt (self->encoder, &self->vinfo,
+ V4L2_PIX_FMT_VP8_FRAME)) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported pixel format"),
+ ("No support for %ux%u format VP8", state->info.width,
+ state->info.height));
+ return FALSE;
+ }
+
+ self->width = state->info.width;
+ self->height = state->info.height;
+ gst_v4l2_codec_vp8_enc_buffers_allocation (encoder);
+
+ if (self->output_state)
+ gst_video_codec_state_unref (self->output_state);
+
+ caps = gst_caps_new_empty_simple ("video/x-vp8");
+
+ self->output_state =
+ gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self),
+ caps, state);
+
+ if (GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) {
+ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SINK)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
+ ("Could not enable the encoder driver."),
+ ("VIDIOC_STREAMON(SINK) failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+
+ if (!gst_v4l2_encoder_streamon (self->encoder, GST_PAD_SRC)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
+ ("Could not enable the encoder driver."),
+ ("VIDIOC_STREAMON(SRC) failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+
+ gst_v4l2_codec_vp8_enc_get_qp_range (self->encoder, &self->qp_min,
+ &self->qp_max);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gst_v4l2_codec_vp8_enc_set_flushing (GstV4l2CodecVp8Enc * self,
+ gboolean flushing)
+{
+ if (self->sink_allocator)
+ gst_v4l2_codec_allocator_set_flushing (self->sink_allocator, flushing);
+ if (self->src_allocator)
+ gst_v4l2_codec_allocator_set_flushing (self->src_allocator, flushing);
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_flush (GstVideoEncoder * encoder)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ GST_DEBUG_OBJECT (self, "Flushing encoder state.");
+
+ gst_v4l2_encoder_flush (self->encoder);
+ gst_v4l2_codec_vp8_enc_set_flushing (self, FALSE);
+
+ return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (encoder);
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_sink_event (GstVideoEncoder * encoder, GstEvent * event)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ GST_DEBUG_OBJECT (self, "flush start");
+ gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE);
+ break;
+ default:
+ break;
+ }
+
+ return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (encoder, event);
+}
+
+static GstStateChangeReturn
+gst_v4l2_codec_vp8_enc_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (element);
+
+ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
+ gst_v4l2_codec_vp8_enc_set_flushing (self, TRUE);
+
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+}
+
+
+static void
+gst_v4l2_codec_vp8_enc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
+ GObject *dec = G_OBJECT (self->encoder);
+
+ switch (prop_id) {
+ default:
+ gst_v4l2_encoder_set_property (dec, prop_id - PROP_LAST, value, pspec);
+ break;
+ }
+}
+
+static void
+gst_v4l2_codec_vp8_enc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
+ GObject *dec = G_OBJECT (self->encoder);
+
+ switch (prop_id) {
+ default:
+ gst_v4l2_encoder_get_property (dec, prop_id - PROP_LAST, value, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_copy_input_buffer (GstV4l2CodecVp8Enc * self,
+ GstVideoCodecFrame * frame)
+{
+ GstVideoFrame src_frame;
+ GstVideoFrame dest_frame;
+ GstVideoInfo dest_vinfo;
+ GstBuffer *buffer;
+ GstFlowReturn flow_ret;
+
+ gst_video_info_set_format (&dest_vinfo, GST_VIDEO_INFO_FORMAT (&self->vinfo),
+ self->width, self->height);
+
+ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->sink_pool),
+ &buffer, NULL);
+ if (flow_ret != GST_FLOW_OK) {
+ if (flow_ret == GST_FLOW_FLUSHING)
+ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing.");
+ else
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("No more picture buffer available."), (NULL));
+ return FALSE;
+ }
+
+ if (!buffer)
+ goto fail;
+
+ if (!gst_video_frame_map (&src_frame, &self->vinfo,
+ frame->input_buffer, GST_MAP_READ))
+ goto fail;
+
+ if (!gst_video_frame_map (&dest_frame, &dest_vinfo, buffer, GST_MAP_WRITE)) {
+ gst_video_frame_unmap (&dest_frame);
+ goto fail;
+ }
+
+ if (!gst_video_frame_copy (&dest_frame, &src_frame)) {
+ gst_video_frame_unmap (&src_frame);
+ gst_video_frame_unmap (&dest_frame);
+ goto fail;
+ }
+
+ gst_video_frame_unmap (&src_frame);
+ gst_video_frame_unmap (&dest_frame);
+ gst_buffer_replace (&frame->input_buffer, buffer);
+ gst_buffer_unref (buffer);
+
+ return TRUE;
+
+fail:
+ GST_ERROR_OBJECT (self, "Failed copy input buffer.");
+ return FALSE;
+}
+
+static gboolean
+gst_v4l2_codec_vp8_enc_ensure_output_bitstream (GstV4l2CodecVp8Enc * self,
+ GstVideoCodecFrame * frame)
+{
+ GstFlowReturn flow_ret;
+
+ flow_ret = gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (self->src_pool),
+ &frame->output_buffer, NULL);
+ if (flow_ret != GST_FLOW_OK) {
+ if (flow_ret == GST_FLOW_FLUSHING)
+ GST_DEBUG_OBJECT (self, "Frame encoding aborted, we are flushing.");
+ else
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("No more encoded buffer available."), (NULL));
+ return FALSE;
+ }
+
+ if (!frame->output_buffer)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gst_v4l2_codec_vp8_enc_fill_encode_params (GstVp8Encoder * encoder,
+ GstVp8Frame * vp8_frame)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+
+ switch (vp8_frame->type) {
+ case GstVp8Keyframe:
+ self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_KEYFRAME;
+ self->encode_params.loop_filter_level = 26;
+ break;
+ case GstVp8Inter:
+ default:
+ self->encode_params.frame_type = V4L2_VP8_FRAME_TYPE_INTER;
+ self->encode_params.loop_filter_level = 12;
+ break;
+ }
+
+ self->encode_params.flags = V4L2_VP8_FRAME_FLAG_SHOWFRAME;
+}
+
+static guint
+gst_v4l2_codec_vp8_enc_check_qp_range (GstV4l2CodecVp8Enc * self,
+ GstVp8Frame * vp8_frame)
+{
+ if (vp8_frame->quality > self->qp_max)
+ return self->qp_max;
+ if (vp8_frame->quality < self->qp_min)
+ return self->qp_min;
+
+ return vp8_frame->quality;
+}
+
+static GstFlowReturn
+gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder,
+ GstVp8Frame * vp8_frame)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (encoder);
+ GstVideoEncoder *venc = GST_VIDEO_ENCODER (encoder);
+ GstV4l2Request *request = NULL;
+ GstFlowReturn ret = GST_FLOW_ERROR;
+ GstVideoCodecFrame *frame = vp8_frame->frame;
+ GstBuffer *resized_buffer;
+ guint32 bytesused;
+
+ /* *INDENT-OFF* */
+ struct v4l2_ext_control control[] = {
+ {
+ .id = V4L2_CID_STATELESS_VP8_ENCODE_PARAMS,
+ .ptr = &self->encode_params,
+ .size = sizeof (self->encode_params),
+ }, {
+ .id = V4L2_CID_STATELESS_VP8_ENCODE_QP,
+ .value = gst_v4l2_codec_vp8_enc_check_qp_range (self, vp8_frame),
+ .size = sizeof (guint),
+ },
+ };
+ /* *INDENT-ON* */
+
+ GST_DEBUG_OBJECT (self, "encode vp8 frame with quality = %d",
+ vp8_frame->quality);
+
+ if (!gst_v4l2_codec_vp8_enc_ensure_output_bitstream (self, frame)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
+ ("Failed to allocate output buffer."), (NULL));
+ goto done;
+ }
+
+ if (!gst_v4l2_codec_vp8_enc_copy_input_buffer (self, frame)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
+ ("Failed to allocate/copy input buffer."), (NULL));
+ goto done;
+ }
+
+ request = gst_v4l2_encoder_alloc_request (self->encoder,
+ frame->system_frame_number, frame->input_buffer, frame->output_buffer);
+
+ if (!request) {
+ GST_ELEMENT_ERROR (self, RESOURCE, NO_SPACE_LEFT,
+ ("Failed to allocate a media request object."), (NULL));
+ goto done;
+ }
+
+ gst_v4l2_codec_vp8_enc_fill_encode_params (encoder, vp8_frame);
+
+ if (!gst_v4l2_encoder_set_controls (self->encoder, request, control,
+ G_N_ELEMENTS (control))) {
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("Driver did not accept the control parameters."), (NULL));
+ goto done;
+ }
+
+ if (!gst_v4l2_encoder_request_queue (request, 0)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("Driver did not accept the encode request."), (NULL));
+ goto done;
+ }
+
+ if (!gst_v4l2_encoder_request_set_done (request, &bytesused)) {
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
+ ("Driver did not ack the request."), (NULL));
+ goto done;
+ }
+
+ gst_v4l2_encoder_request_unref (request);
+
+ resized_buffer = gst_buffer_copy_region (frame->output_buffer,
+ GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_DEEP, 0, bytesused);
+ gst_buffer_replace (&frame->output_buffer, resized_buffer);
+ gst_buffer_unref (resized_buffer);
+
+ return gst_video_encoder_finish_frame (venc, frame);
+
+done:
+ if (request)
+ gst_v4l2_encoder_request_unref (request);
+
+ return ret;
+}
+
+static void
+gst_v4l2_codec_vp8_enc_init (GstV4l2CodecVp8Enc * self)
+{
+}
+
+static void
+gst_v4l2_codec_vp8_enc_subinit (GstV4l2CodecVp8Enc * self,
+ GstV4l2CodecVp8EncClass * klass)
+{
+ self->encoder = gst_v4l2_encoder_new (klass->device);
+}
+
+static void
+gst_v4l2_codec_vp8_enc_dispose (GObject * object)
+{
+ GstV4l2CodecVp8Enc *self = GST_V4L2_CODEC_VP8_ENC (object);
+
+ g_clear_object (&self->encoder);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_v4l2_codec_vp8_enc_class_init (GstV4l2CodecVp8EncClass * klass)
+{
+}
+
+static void
+gst_v4l2_codec_vp8_enc_subclass_init (GstV4l2CodecVp8EncClass * klass,
+ GstV4l2CodecDevice * device)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
+ GstVp8EncoderClass *vp8encoder_class = GST_VP8_ENCODER_CLASS (klass);
+
+ gobject_class->set_property = gst_v4l2_codec_vp8_enc_set_property;
+ gobject_class->get_property = gst_v4l2_codec_vp8_enc_get_property;
+ gobject_class->dispose = gst_v4l2_codec_vp8_enc_dispose;
+
+ gst_element_class_set_static_metadata (element_class,
+ "V4L2 Stateless VP8 Video Encoder",
+ "Codec/Encoder/Video/Hardware",
+ "A V4L2 based VP8 video encoder",
+ "Benjamin Gaignard <benjamin.gaignard@collabora.com>");
+
+ gst_element_class_add_static_pad_template (element_class, &sink_template);
+ gst_element_class_add_static_pad_template (element_class, &src_template);
+
+ element_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_change_state);
+
+ encoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_open);
+ encoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_close);
+ encoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_start);
+ encoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_stop);
+ encoder_class->set_format =
+ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_set_format);
+ encoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_flush);
+ encoder_class->sink_event =
+ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_sink_event);
+ encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_getcaps);
+ vp8encoder_class->encode_frame =
+ GST_DEBUG_FUNCPTR (gst_v4l2_codec_vp8_enc_encode_frame);
+
+ klass->device = device;
+ gst_v4l2_encoder_install_properties (gobject_class, PROP_LAST, device);
+}
+
+void
+gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin, GstV4l2Encoder * encoder,
+ GstV4l2CodecDevice * device, guint rank)
+{
+ gchar *element_name;
+ guint version;
+
+ GST_DEBUG_CATEGORY_INIT (v4l2_vp8enc_debug, "v4l2codecs-vp8enc", 0,
+ "V4L2 stateless VP8 encoder");
+
+ version = gst_v4l2_encoder_get_version (encoder);
+ if (version < V4L2_MIN_KERNEL_VERSION)
+ GST_WARNING ("V4L2 API v%u.%u too old, at least v%u.%u required",
+ (version >> 16) & 0xff, (version >> 8) & 0xff,
+ V4L2_MIN_KERNEL_VER_MAJOR, V4L2_MIN_KERNEL_VER_MINOR);
+
+ if (!gst_v4l2_codec_vp8_enc_api_check (encoder)) {
+ GST_WARNING ("Not registering VP8 encoder as it failed ABI check.");
+ return;
+ }
+
+ gst_v4l2_encoder_register (plugin, GST_TYPE_V4L2_CODEC_VP8_ENC,
+ (GClassInitFunc) gst_v4l2_codec_vp8_enc_subclass_init,
+ gst_mini_object_ref (GST_MINI_OBJECT (device)),
+ (GInstanceInitFunc) gst_v4l2_codec_vp8_enc_subinit,
+ "v4l2sl%svp8enc", device, rank, &element_name);
+}
diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.h b/sys/v4l2codecs/gstv4l2codecvp8enc.h
new file mode 100644
index 0000000..115b147
--- /dev/null
+++ b/sys/v4l2codecs/gstv4l2codecvp8enc.h
@@ -0,0 +1,54 @@
+/* GStreamer
+ * Copyright (C) 2022 Benjamin Gaignard <benjamin.gaignard@collabora.com>
+ *
+ * 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_V4L2_CODEC_VP8_ENC_H__
+#define __GST_V4L2_CODEC_VP8_ENC_H__
+
+#define GST_USE_UNSTABLE_API
+#include <gst/codecs/gstvp8encoder.h>
+
+#include "gstv4l2encoder.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_V4L2_CODEC_VP8_ENC (gst_v4l2_codec_vp8_enc_get_type())
+#define GST_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8Enc))
+#define GST_V4L2_CODEC_VP8_ENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2_CODEC_VP8_ENC,GstV4l2CodecVp8EncClass))
+#define GST_V4L2_CODEC_VP8_ENC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_CODEC_VP8_ENC, GstV4l2CodecVp8EncClass))
+#define GST_IS_V4L2_CODEC_VP8_ENC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_CODEC_VP8_ENC))
+#define GST_IS_V4L2_CODEC_VP8_ENC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_CODEC_VP8_ENC))
+
+typedef struct _GstV4l2CodecVp8Enc GstV4l2CodecVp8Enc;
+typedef struct _GstV4l2CodecVp8EncClass GstV4l2CodecVp8EncClass;
+
+struct _GstV4l2CodecVp8EncClass
+{
+ GstVp8EncoderClass parent_class;
+ GstV4l2CodecDevice *device;
+};
+
+GType gst_v4l2_codec_vp8_enc_get_type (void);
+void gst_v4l2_codec_vp8_enc_register (GstPlugin * plugin,
+ GstV4l2Encoder * encoder,
+ GstV4l2CodecDevice * device,
+ guint rank);
+
+G_END_DECLS
+
+#endif /* __GST_V4L2_CODEC_VP8_ENC_H__ */
diff --git a/sys/v4l2codecs/meson.build b/sys/v4l2codecs/meson.build
index 53e8923..638c578 100644
--- a/sys/v4l2codecs/meson.build
+++ b/sys/v4l2codecs/meson.build
@@ -12,6 +12,7 @@ v4l2codecs_sources = [
'gstv4l2format.c',
'gstv4l2codecalphadecodebin.c',
'gstv4l2encoder.c',
+ 'gstv4l2codecvp8enc.c',
]
libgudev_dep = dependency('gudev-1.0', required: get_option('v4l2codecs'))
--
2.25.1

View File

@@ -0,0 +1,76 @@
From 9f38752fa28cb43b1069fe6fa9ce7e1db5958962 Mon Sep 17 00:00:00 2001
From: Benjamin Gaignard <benjamin.gaignard@collabora.com>
Date: Mon, 16 Jan 2023 17:40:30 +0100
Subject: [PATCH 5/5] v4l2codecs: Register V4L2 stateless Vp8 encoder
If a v4l2 stateless VP8 encoder is detected register the matching
element.
---
sys/v4l2codecs/plugin.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/sys/v4l2codecs/plugin.c b/sys/v4l2codecs/plugin.c
index ac83e1b..7867474 100644
--- a/sys/v4l2codecs/plugin.c
+++ b/sys/v4l2codecs/plugin.c
@@ -27,8 +27,10 @@
#include "gstv4l2codech265dec.h"
#include "gstv4l2codecmpeg2dec.h"
#include "gstv4l2codecvp8dec.h"
+#include "gstv4l2codecvp8enc.h"
#include "gstv4l2codecvp9dec.h"
#include "gstv4l2decoder.h"
+#include "gstv4l2encoder.h"
#include "linux/v4l2-controls.h"
#include "linux/media.h"
@@ -91,6 +93,36 @@ register_video_decoder (GstPlugin * plugin, GstV4l2CodecDevice * device)
g_object_unref (decoder);
}
+static void
+register_video_encoder (GstPlugin * plugin, GstV4l2CodecDevice * device)
+{
+ GstV4l2Encoder *encoder = gst_v4l2_encoder_new (device);
+ gint i;
+ guint32 fmt;
+
+ if (!gst_v4l2_encoder_open (encoder)) {
+ g_object_unref (encoder);
+ return;
+ }
+
+ for (i = 0; gst_v4l2_encoder_enum_src_formats (encoder, i, &fmt); i++) {
+ switch (fmt) {
+ case V4L2_PIX_FMT_VP8_FRAME:
+ GST_INFO_OBJECT (encoder, "Registering %s as VP8 Encoder",
+ device->name);
+ gst_v4l2_codec_vp8_enc_register (plugin, encoder, device,
+ GST_RANK_PRIMARY + 1);
+ break;
+ default:
+ GST_FIXME_OBJECT (encoder, "%" GST_FOURCC_FORMAT " is not supported.",
+ GST_FOURCC_ARGS (fmt));
+ break;
+ }
+ }
+
+ g_object_unref (encoder);
+}
+
static gboolean
plugin_init (GstPlugin * plugin)
{
@@ -112,6 +144,9 @@ plugin_init (GstPlugin * plugin)
if (device->function == MEDIA_ENT_F_PROC_VIDEO_DECODER)
register_video_decoder (plugin, device);
+
+ if (device->function == MEDIA_ENT_F_PROC_VIDEO_ENCODER)
+ register_video_encoder (plugin, device);
}
gst_v4l2_codec_device_list_free (devices);
--
2.25.1

View File

@@ -0,0 +1,62 @@
From 5cf87e34ad0aa98ceb004f68ea72498f490e6858 Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@st.com>
Date: Wed, 12 Dec 2018 12:04:03 +0100
Subject: [PATCH 08/14] waylandsink: HACK: disable frame dropping while redraw
is pending
This workaround is needed to reach 30fps video playback.
Redraw callback is received late, leading to many frames
being dropped and so low framerate on display driver stage.
The fact that redraw callback is received late is not yet understood
but display subsystem is able to sustain a higher framerate
than the one reached with redraw callback mechanism.
This workaround consists to ignore redraw callback allowing to
get back to the expected framerate performances.
Even if not observed yet, there is a risk of visible decoding artefacts
due to the fact that downstream elements could rewrite in a frame
being currently rendered by display subsystem...
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
ext/wayland/gstwaylandsink.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index a5fb0cd..d98c53f 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -806,10 +806,29 @@ render_last_buffer (GstWaylandSink * self, gboolean redraw)
wlbuffer = gst_buffer_get_wl_buffer (self->display, self->last_buffer);
surface = gst_wl_window_get_wl_surface (self->window);
+ /*
+ * HACK: disable frame dropping while redraw is pending
+ *
+ * This workaround is needed to reach 30fps video playback.
+ *
+ * Redraw callback is received late, leading to many frames
+ * being dropped and so low framerate on display driver stage.
+ * The fact that redraw callback is received late is not yet understood
+ * but display subsystem is able to sustain a higher framerate
+ * than the one reached with redraw callback mechanism.
+ * This workaround consists to ignore redraw callback allowing to
+ * get back to the expected framerate performances.
+ *
+ * Why not observed yet, there is a risk of visible decoding artefacts
+ * due to the fact that downstream elements could rewrite in a frame
+ * being currently rendered by display subsystem...
+ */
+#if 0
self->redraw_pending = TRUE;
callback = wl_surface_frame (surface);
self->callback = callback;
wl_callback_add_listener (callback, &frame_callback_listener, self);
+#endif
if (G_UNLIKELY (self->video_info_changed && !redraw)) {
info = &self->video_info;
--
2.25.1

View File

@@ -0,0 +1,106 @@
From cf7bb1dd6061de058c33dbc9cd268441af784309 Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Tue, 8 Aug 2023 16:13:35 +0200
Subject: [PATCH] v4l2codecs: add key frame signaling
Propagate V4L2 keyframe flags signaling to upstream elements
by setting GstBuffer delta-unit flag & GstVideoCodecFrame sync point flag.
Signed-off-by: Hugues Fruchet <hugues.fruchet@foss.st.com>
---
sys/v4l2codecs/gstv4l2codecvp8enc.c | 11 ++++++++++-
sys/v4l2codecs/gstv4l2encoder.c | 7 ++++---
sys/v4l2codecs/gstv4l2encoder.h | 2 +-
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/sys/v4l2codecs/gstv4l2codecvp8enc.c b/sys/v4l2codecs/gstv4l2codecvp8enc.c
index 2f15caf..71c7dd2 100644
--- a/sys/v4l2codecs/gstv4l2codecvp8enc.c
+++ b/sys/v4l2codecs/gstv4l2codecvp8enc.c
@@ -542,6 +542,7 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder,
GstVideoCodecFrame *frame = vp8_frame->frame;
GstBuffer *resized_buffer;
guint32 bytesused;
+ guint32 flags;
/* *INDENT-OFF* */
struct v4l2_ext_control control[] = {
@@ -596,7 +597,7 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder,
goto done;
}
- if (!gst_v4l2_encoder_request_set_done (request, &bytesused)) {
+ if (!gst_v4l2_encoder_request_set_done (request, &bytesused, &flags)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE,
("Driver did not ack the request."), (NULL));
goto done;
@@ -609,6 +610,14 @@ gst_v4l2_codec_vp8_enc_encode_frame (GstVp8Encoder * encoder,
gst_buffer_replace (&frame->output_buffer, resized_buffer);
gst_buffer_unref (resized_buffer);
+ if (flags & V4L2_BUF_FLAG_KEYFRAME) {
+ GST_BUFFER_FLAG_UNSET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+ GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
+ } else {
+ GST_BUFFER_FLAG_SET (frame->output_buffer, GST_BUFFER_FLAG_DELTA_UNIT);
+ GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
+ }
+
return gst_video_encoder_finish_frame (venc, frame);
done:
diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c
index 5ae2cfa..e2ef423 100644
--- a/sys/v4l2codecs/gstv4l2encoder.c
+++ b/sys/v4l2codecs/gstv4l2encoder.c
@@ -696,7 +696,7 @@ gst_v4l2_encoder_dequeue_sink (GstV4l2Encoder * self)
static gboolean
gst_v4l2_encoder_dequeue_src (GstV4l2Encoder * self, guint32 * out_frame_num,
- guint32 * bytesused)
+ guint32 * bytesused, guint32 * flags)
{
gint ret;
struct v4l2_plane planes[GST_VIDEO_MAX_PLANES] = { {0} };
@@ -718,6 +718,7 @@ gst_v4l2_encoder_dequeue_src (GstV4l2Encoder * self, guint32 * out_frame_num,
*out_frame_num = buf.timestamp.tv_usec;
*bytesused = buf.m.planes[0].bytesused;
+ *flags = buf.flags;
GST_TRACE_OBJECT (self, "Dequeued bitstream buffer %i, %d bytes used",
buf.index, buf.m.planes[0].bytesused);
@@ -1137,7 +1138,7 @@ gst_v4l2_encoder_request_queue (GstV4l2Request * request, guint flags)
gint
gst_v4l2_encoder_request_set_done (GstV4l2Request * request,
- guint32 * bytesused)
+ guint32 * bytesused, guint32 * flags)
{
GstV4l2Encoder *encoder = request->encoder;
GstV4l2Request *pending_req = NULL;
@@ -1167,7 +1168,7 @@ gst_v4l2_encoder_request_set_done (GstV4l2Request * request,
if (!pending_req->hold_pic_buf) {
guint32 frame_num = G_MAXUINT32;
- if (!gst_v4l2_encoder_dequeue_src (encoder, &frame_num, bytesused)) {
+ if (!gst_v4l2_encoder_dequeue_src (encoder, &frame_num, bytesused, flags)) {
pending_req->failed = TRUE;
} else if (frame_num != pending_req->frame_num) {
GST_WARNING_OBJECT (encoder,
diff --git a/sys/v4l2codecs/gstv4l2encoder.h b/sys/v4l2codecs/gstv4l2encoder.h
index 7ff01a9..9acb0e3 100644
--- a/sys/v4l2codecs/gstv4l2encoder.h
+++ b/sys/v4l2codecs/gstv4l2encoder.h
@@ -128,7 +128,7 @@ void gst_v4l2_encoder_ro_request_unref (GstV4l2Request * request);
gboolean gst_v4l2_encoder_request_queue (GstV4l2Request * request,
guint flags);
-gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused);
+gint gst_v4l2_encoder_request_set_done (GstV4l2Request * request, guint32 * bytesused, guint32 * flags);
gboolean gst_v4l2_encoder_request_failed (GstV4l2Request * request);
--
2.25.1

View File

@@ -0,0 +1,62 @@
From 3ffaca250df9e72917ed3306d538474f44d7b1e2 Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Thu, 17 Aug 2023 11:10:54 +0200
Subject: [PATCH 2/2] gtkwaylandsink: HACK: disable frame dropping while redraw
is pending
This workaround is needed to reach 30fps video playback.
Redraw callback is received late, leading to many frames
being dropped and so low framerate on display driver stage.
The fact that redraw callback is received late is not yet understood
but display subsystem is able to sustain a higher framerate
than the one reached with redraw callback mechanism.
This workaround consists to ignore redraw callback allowing to
get back to the expected framerate performances.
Even if not observed yet, there is a risk of visible decoding artefacts
due to the fact that downstream elements could rewrite in a frame
being currently rendered by display subsystem...
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
ext/gtk/gstgtkwaylandsink.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 26e4aa0..88cbe5d 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -1095,10 +1095,29 @@ render_last_buffer (GstGtkWaylandSink * self, gboolean redraw)
wlbuffer = gst_buffer_get_wl_buffer (priv->display, priv->last_buffer);
surface = gst_wl_window_get_wl_surface (priv->wl_window);
+ /*
+ * HACK: disable frame dropping while redraw is pending
+ *
+ * This workaround is needed to reach 30fps video playback.
+ *
+ * Redraw callback is received late, leading to many frames
+ * being dropped and so low framerate on display driver stage.
+ * The fact that redraw callback is received late is not yet understood
+ * but display subsystem is able to sustain a higher framerate
+ * than the one reached with redraw callback mechanism.
+ * This workaround consists to ignore redraw callback allowing to
+ * get back to the expected framerate performances.
+ *
+ * Why not observed yet, there is a risk of visible decoding artefacts
+ * due to the fact that downstream elements could rewrite in a frame
+ * being currently rendered by display subsystem...
+ */
+#if 0
priv->redraw_pending = TRUE;
callback = wl_surface_frame (surface);
priv->callback = callback;
wl_callback_add_listener (callback, &frame_callback_listener, self);
+#endif
if (G_UNLIKELY (priv->video_info_changed && !redraw)) {
info = &priv->video_info;
--
2.25.1

View File

@@ -0,0 +1,62 @@
From ec109f9a3b50f21227e15f45b1558b0f5c6f73ef Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Wed, 16 Aug 2023 17:14:55 +0200
Subject: [PATCH 1/2] gtkwaylandsink: cancel pending redraw callback on
pause/resume
Pipeline freeze have been observed when doing intensive pause/resume
on video playback.
Analysis shows that gtkwaylandsink element is stalled on state change
from PLAYING to PAUSED or PAUSED to PLAYING.
There is a first problem when ASYNC state change is returned by parent
class; in this case we prematurely return from state_change, fix that by
checking GST_STATE_CHANGE_FAILURE instead.
The other problem is about eventual pending wayland listener callback
(see redraw_callback mechanism) while changing state which cause
further freeze of element. Fix this by destroying listener callback
and resetting redraw_callback flag when going from PLAYING to PAUSED and
PAUSED to PLAYING.
Signed-off-by: Hugues Fruchet <hugues.fruchet@foss.st.com>
---
ext/gtk/gstgtkwaylandsink.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 0d6ea69..26e4aa0 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -736,7 +736,7 @@ gst_gtk_wayland_sink_change_state (GstElement * element,
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
- if (ret != GST_STATE_CHANGE_SUCCESS)
+ if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
@@ -762,6 +762,18 @@ gst_gtk_wayland_sink_change_state (GstElement * element,
priv->redraw_pending = FALSE;
g_mutex_unlock (&priv->render_lock);
break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ /* Destroy pending redraw callback otherwise
+ * element may freeze */
+ g_mutex_lock (&priv->render_lock);
+ if (priv->callback) {
+ wl_callback_destroy (priv->callback);
+ priv->callback = NULL;
+ }
+ priv->redraw_pending = FALSE;
+ g_mutex_unlock (&priv->render_lock);
+ break;
default:
break;
}
--
2.25.1

View File

@@ -0,0 +1,34 @@
From ee75c40eff3ed516535e8b150e5a17206e73d81a Mon Sep 17 00:00:00 2001
From: Hugues Fruchet <hugues.fruchet@foss.st.com>
Date: Wed, 30 Aug 2023 10:48:22 +0200
Subject: [PATCH 1/3] v4l2codecs: fix support of unaligned videos
Current implementation fails on frame input mapping when
driver returns an aligned width & height.
Fix this by ignoring aligned width & height returned by S_FMT,
it is useless till buffer alignment is extrapolated later on
from bytesperline and sizeimage S_FMT values.
Signed-off-by: Hugues Fruchet <hugues.fruchet@foss.st.com>
---
sys/v4l2codecs/gstv4l2encoder.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/sys/v4l2codecs/gstv4l2encoder.c b/sys/v4l2codecs/gstv4l2encoder.c
index e2ef423..eabb74a 100644
--- a/sys/v4l2codecs/gstv4l2encoder.c
+++ b/sys/v4l2codecs/gstv4l2encoder.c
@@ -415,6 +415,10 @@ gst_v4l2_encoder_select_sink_format (GstV4l2Encoder * self, GstVideoInfo * in,
}
}
+
+ fmt.fmt.pix_mp.width = width;
+ fmt.fmt.pix_mp.height = height;
+
if (!gst_v4l2_format_to_video_info (&fmt, out)) {
GST_ERROR_OBJECT (self, "Unsupported V4L2 pixelformat %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (fmt.fmt.pix_mp.pixelformat));
--
2.25.1

View File

@@ -0,0 +1,53 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI:append = " \
file://0001-kmsallocator-Port-to-the-new-DRM-Dumb-Allocator.patch \
file://0002-gtkwaylandsink-Remove-redefine-of-GST_CAPS_FEATURE_M.patch \
file://0003-waylandsink-Stop-modifying-the-display-GstVideoInfo.patch \
file://0004-gtkwaylandsink-Force-a-redraw-on-resolution-change.patch \
file://0005-waylandsink-Let-the-baseclass-know-when-frames-are-d.patch \
file://0006-waylandsink-Refactor-internal-pool-handling.patch \
file://0007-gtkwaylandsink-Fix-display-wl_window-pool-leaks.patch \
file://0008-wllinuxdmabuf-Handle-video-meta-inside-the-importer.patch \
file://0009-wlvideoformat-Fix-sign-issue-for-DRM-fourcc.patch \
file://0010-wlvideobufferpool-Add-DRM-Dumb-buffer-support.patch \
file://0011-wayladnsink-Add-DRM-Dumb-allocator-support.patch \
file://0012-bad-Update-doc-cache-for-waylandsink-changes.patch \
file://0014-WAYLANDSINK-use-card0a-as-default-drm-device.patch \
file://0015-waylandsink-Uprank-to-secondary.patch \
file://0016-gstwlshmallocator-correct-WL-API-declaration.patch \
file://0017-gtkwaylandsink-Destroy-GstWlWindow-when-par.patch \
file://0018-GTKWAYLANDSINK-use-card0-as-default-drm-device.patch \
file://0019-waylandsink-Emit-map-signal-boarder-surface-is-ready.patch \
file://0020-gtkwaylandsink-do-not-use-drm-dumb-pool-with-DMAbuf-.patch \
file://0022-codecs-Add-base-class-for-stateless-vp8-encoder.patch \
file://0023-v4l2codecs-Add-V4L2-VP8-stateless-encode-uAPI.patch \
file://0024-v4l2codecs-Add-v4l2-encoder-class.patch \
file://0025-v4l2codecs-Add-V4L2-stateless-VP8-encoder.patch \
file://0026-v4l2codecs-Register-V4L2-stateless-Vp8-encoder.patch \
file://0027-waylandsink-HACK-disable-frame-dropping-while-redraw.patch \
file://0028-v4l2codecs-add-key-frame-signaling.patch \
file://0029-gtkwaylandsink-HACK-disable-frame-dropping-while-red.patch \
file://0030-gtkwaylandsink-cancel-pending-redraw-callback-on-pau.patch \
file://0031-v4l2codecs-fix-support-of-unaligned-videos.patch \
"
PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}"
PACKAGECONFIG[gtk3] = "-Dgtk3=enabled,-Dgtk3=disabled,gtk+3"
PACKAGECONFIG ?= " \
${GSTREAMER_ORC} \
${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \
${@bb.utils.filter('DISTRO_FEATURES', 'directfb vulkan', d)} \
${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland gtk3', '', d)} \
${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gl', '', d)} \
bz2 closedcaption curl dash dtls hls rsvg sbc smoothstreaming sndfile \
ttml uvch264 webp \
faac kms \
v4l2codecs \
"
do_install:append() {
install -d ${D}${includedir}/gstreamer-1.0/wayland
install -m 644 ${S}/gst-libs/gst/wayland/wayland.h ${D}${includedir}/gstreamer-1.0/wayland
}

View File

@@ -0,0 +1,954 @@
From 3923d3d4c64870a02f6f219c3444fdcb196dca23 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 8 Feb 2023 16:50:10 -0500
Subject: [PATCH] allocators: Add a DRM Dumb Allocator
This allow allocating memory from any DRM driver that supports this
method. It additionally allow exporting DMABuf. This allocator depends
on libdrm and will be stubbed if the dependency is missing. This is derived
from kmssink dumb allocator.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
gst-libs/gst/allocators/allocators.h | 1 +
gst-libs/gst/allocators/gstdrmdumb.c | 755 +++++++++++++++++++++++++++
gst-libs/gst/allocators/gstdrmdumb.h | 88 ++++
gst-libs/gst/allocators/meson.build | 7 +-
meson.build | 7 +
meson_options.txt | 1 +
6 files changed, 856 insertions(+), 3 deletions(-)
create mode 100644 gst-libs/gst/allocators/gstdrmdumb.c
create mode 100644 gst-libs/gst/allocators/gstdrmdumb.h
diff --git a/gst-libs/gst/allocators/allocators.h b/gst-libs/gst/allocators/allocators.h
index b616aa2..65e7e81 100644
--- a/gst-libs/gst/allocators/allocators.h
+++ b/gst-libs/gst/allocators/allocators.h
@@ -27,6 +27,7 @@
#include <gst/allocators/gstdmabuf.h>
#include <gst/allocators/gstfdmemory.h>
#include <gst/allocators/gstphysmemory.h>
+#include <gst/allocators/gstdrmdumb.h>
#endif /* __GST_ALLOCATORS_H__ */
diff --git a/gst-libs/gst/allocators/gstdrmdumb.c b/gst-libs/gst/allocators/gstdrmdumb.c
new file mode 100644
index 0000000..c5c4834
--- /dev/null
+++ b/gst-libs/gst/allocators/gstdrmdumb.c
@@ -0,0 +1,755 @@
+/* GStreamer
+ *
+ * Copyright (C) 2023 Collabora
+ * Copyright (C) 2016 Igalia
+ *
+ * Authors:
+ * Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
+ * Javier Martin <javiermartin@by.com.es>
+ * Colin Kinloch <colin.kinloch@collabora.com>
+ *
+ * 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 "gstdrmdumb.h"
+#include "gstdmabuf.h"
+
+/**
+ * SECTION:gstdrmdumb
+ * @title: GstDRMDumbAllocator
+ * @short_description: Memory wrapper for Linux DRM Dumb memory
+ * @see_also: #GstMemory
+ *
+ * Since: 1.24
+ */
+
+#ifdef HAVE_LIBDRM
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <drm.h>
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#endif
+
+#define GST_CAT_DEFAULT drmdumballocator_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+#define GST_DRM_DUMB_MEMORY_TYPE "DRMDumbMemory"
+
+typedef struct _GstDRMDumbMemory GstDRMDumbMemory;
+struct _GstDRMDumbMemory
+{
+ GstMemory parent;
+
+ gpointer ptr;
+ gsize size;
+ guint32 handle;
+ guint refs;
+};
+
+struct _GstDRMDumbAllocator
+{
+ GstAllocator parent;
+
+ gint drm_fd;
+ gchar *drm_device_path;
+
+ /* protected by GstDRMDumbAllocator object lock */
+ GstAllocator *dmabuf_alloc;
+};
+
+#define parent_class gst_drm_dumb_allocator_parent_class
+G_DEFINE_TYPE_WITH_CODE (GstDRMDumbAllocator, gst_drm_dumb_allocator,
+ GST_TYPE_ALLOCATOR,
+ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "drmdumballocator", 0,
+ "DRM dumb buffer allocator"));
+enum
+{
+ PROP_DRM_FD = 1,
+ PROP_DRM_DEVICE_PATH,
+ PROP_N,
+};
+
+static GParamSpec *g_props[PROP_N] = { NULL, };
+
+/**
+ * gst_is_drm_dumb_memory:
+ * @mem: the memory to be checked
+ *
+ * Returns: %TRUE if @mem is DRM Dumb memory, otherwise %FALSE
+ *
+ * Since: 1.24
+ */
+gboolean
+gst_is_drm_dumb_memory (GstMemory * mem)
+{
+ return gst_memory_is_type (mem, GST_DRM_DUMB_MEMORY_TYPE);
+}
+
+/**
+ * gst_drm_dumb_memory_get_handle:
+ * @mem: the memory to get the handle from
+ *
+ * Return the DRM buffer object handle associated with @mem.
+ *
+ * Returns: the DRM buffer object handle associated with the memory, or 0.
+ * The handle is still owned by the GstMemory and cannot be used
+ * beyond the lifetime of this GstMemory unless it is being passed
+ * to DRM driver, which does handle a refcount internally.
+ *
+ * Since: 1.24
+ */
+guint32
+gst_drm_dumb_memory_get_handle (GstMemory * mem)
+{
+ if (!gst_is_drm_dumb_memory (mem))
+ return 0;
+
+ return ((GstDRMDumbMemory *) mem)->handle;
+}
+
+/**
+ * gst_drm_dumb_memory_export_dmabuf:
+ * @mem: the memory to export from
+ *
+ * Exports a DMABuf from the DRM Bumb buffer object. One can check if this
+ * feature is supported using gst_drm_dumb_allocator_has_prime_export();
+ *
+ * Returns: a #GstMemory from #GstDmaBufAllocator wrapping the exported dma-buf
+ * file descriptor.
+ *
+ * Since: 1.24
+ */
+GstMemory *
+gst_drm_dumb_memory_export_dmabuf (GstMemory * mem)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbMemory *drmmem = (GstDRMDumbMemory *) mem;
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (mem->allocator);
+ GstMemory *dmamem;
+ gint ret;
+ gint prime_fd;
+
+ ret = drmPrimeHandleToFD (alloc->drm_fd, drmmem->handle,
+ DRM_CLOEXEC | DRM_RDWR, &prime_fd);
+ if (ret)
+ goto export_fd_failed;
+
+ if (G_UNLIKELY (alloc->dmabuf_alloc == NULL))
+ alloc->dmabuf_alloc = gst_dmabuf_allocator_new ();
+
+ dmamem = gst_dmabuf_allocator_alloc (alloc->dmabuf_alloc, prime_fd,
+ gst_memory_get_sizes (mem, NULL, NULL));
+
+ GST_DEBUG_OBJECT (alloc, "Exported bo handle %d as %d", drmmem->handle,
+ prime_fd);
+
+ return dmamem;
+
+ /* ERRORS */
+export_fd_failed:
+ {
+ GST_ERROR_OBJECT (alloc, "Failed to export bo handle %d: %s (%d)",
+ drmmem->handle, g_strerror (errno), ret);
+ return NULL;
+ }
+
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBDRM
+static guint32
+gst_drm_height_from_drm (guint32 drmfmt, guint32 height)
+{
+ guint32 ret;
+
+ switch (drmfmt) {
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_P010:
+ case DRM_FORMAT_P016:
+ ret = height * 3 / 2;
+ break;
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ ret = height * 2;
+ break;
+ case DRM_FORMAT_NV24:
+ ret = height * 3;
+ break;
+ default:
+ ret = height;
+ break;
+ }
+
+ return ret;
+}
+
+static guint32
+gst_drm_bpp_from_drm (guint32 drm_fourcc)
+{
+ guint32 bpp;
+
+ switch (drm_fourcc) {
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_NV24:
+ bpp = 8;
+ break;
+ case DRM_FORMAT_P010:
+ bpp = 10;
+ break;
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_P016:
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ bpp = 16;
+ break;
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_RGB888:
+ bpp = 24;
+ break;
+ default:
+ bpp = 32;
+ break;
+ }
+
+ return bpp;
+}
+
+static gboolean
+check_drm_fd (GstDRMDumbAllocator * alloc)
+{
+ return alloc->drm_fd > -1;
+}
+#endif
+
+static void
+gst_drm_dumb_allocator_open_device (GstDRMDumbAllocator * alloc,
+ const gchar * path)
+{
+#ifdef HAVE_LIBDRM
+ gint fd = -1;
+
+ /* Ignore default constructor call */
+ if (path == NULL)
+ return;
+
+ /* construct only */
+ g_assert (alloc->drm_fd == -1);
+ g_assert (alloc->drm_device_path == NULL);
+
+ fd = open (path, O_RDWR | O_CLOEXEC);
+
+ if (fd < 0) {
+ GST_WARNING_OBJECT (alloc, "Failed to open DRM device at %s", path);
+ return;
+ }
+
+ alloc->drm_device_path = g_strdup (path);
+ alloc->drm_fd = fd;
+#endif
+}
+
+static void
+gst_drm_dumb_allocator_set_fd (GstDRMDumbAllocator * alloc, gint fd)
+{
+#ifdef HAVE_LIBDRM
+ /* Ignore default constructor call */
+ if (fd == -1)
+ return;
+
+ /* construct only */
+ g_assert (alloc->drm_fd == -1);
+ g_assert (alloc->drm_device_path == NULL);
+
+ if (fd >= 0) {
+ alloc->drm_device_path = drmGetDeviceNameFromFd2 (fd);
+ if (!alloc->drm_device_path) {
+ GST_WARNING_OBJECT (alloc, "Failed to verify DRM fd.");
+ return;
+ }
+
+ GST_DEBUG_OBJECT (alloc, "Using external FD for %s",
+ alloc->drm_device_path);
+
+ alloc->drm_fd = dup (fd);
+ }
+#endif
+}
+
+static void
+gst_drm_dumb_allocator_memory_reset (GstDRMDumbAllocator * alloc,
+ GstDRMDumbMemory * mem)
+{
+#ifdef HAVE_LIBDRM
+ gint err;
+ struct drm_mode_destroy_dumb arg = { 0, };
+
+ if (!mem->size)
+ return;
+
+ if (!check_drm_fd (alloc))
+ return;
+
+ if (mem->ptr != NULL) {
+ GST_WARNING_OBJECT (alloc, "destroying mapped bo (refcount=%d)", mem->refs);
+ munmap (mem->ptr, mem->size);
+ mem->ptr = NULL;
+ }
+
+ arg.handle = mem->handle;
+
+ err = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg);
+ if (err)
+ GST_WARNING_OBJECT (alloc,
+ "Failed to destroy dumb buffer object: %s %d",
+ g_strerror (errno), errno);
+
+ mem->handle = -1;
+ mem->size = 0;
+#endif
+}
+
+static gboolean
+gst_drm_dumb_allocator_memory_create (GstDRMDumbAllocator * alloc,
+ GstDRMDumbMemory * drmmem,
+ guint32 drm_fourcc, guint32 width, guint32 height, guint32 * out_pitch)
+{
+#ifdef HAVE_LIBDRM
+ gint ret;
+ struct drm_mode_create_dumb arg = { 0, };
+
+ if (drmmem->size)
+ return TRUE;
+
+ if (!check_drm_fd (alloc))
+ return FALSE;
+
+ arg.bpp = gst_drm_bpp_from_drm (drm_fourcc);
+ arg.width = width;
+ arg.height = gst_drm_height_from_drm (drm_fourcc, height);
+
+ ret = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
+ if (ret)
+ goto create_failed;
+
+ if (!arg.pitch) {
+ GST_DEBUG_OBJECT (alloc,
+ "DRM dumb buffer pitch not set, no need to modify vinfo");
+ goto done;
+ }
+
+ GST_DEBUG_OBJECT (alloc,
+ "DRM dumb buffer pitch is set, vinfo modification required");
+ *out_pitch = arg.pitch;
+
+done:
+ drmmem->handle = arg.handle;
+ /* will be used used as maxsize of GstMemory */
+ drmmem->size = arg.size;
+
+ return TRUE;
+
+ /* ERRORS */
+create_failed:
+ {
+ GST_ERROR_OBJECT (alloc, "Failed to create buffer object: %s (%d)",
+ g_strerror (errno), errno);
+ return FALSE;
+ }
+
+#else
+ return FALSE;
+#endif
+}
+
+static void
+gst_drm_dumb_allocator_free (GstAllocator * base_alloc, GstMemory * mem)
+{
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc);
+ GstDRMDumbMemory *drmmem;
+
+ drmmem = (GstDRMDumbMemory *) mem;
+
+ gst_drm_dumb_allocator_memory_reset (alloc, drmmem);
+ g_free (drmmem);
+}
+
+static void
+gst_drm_dumb_allocator_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (object);
+
+ switch (prop_id) {
+ case PROP_DRM_FD:
+ gst_drm_dumb_allocator_set_fd (alloc, g_value_get_int (value));
+ break;
+ case PROP_DRM_DEVICE_PATH:
+ gst_drm_dumb_allocator_open_device (alloc, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_drm_dumb_allocator_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (object);
+
+ switch (prop_id) {
+ case PROP_DRM_FD:
+ g_value_set_int (value, alloc->drm_fd);
+ break;
+ case PROP_DRM_DEVICE_PATH:
+ g_value_set_string (value, alloc->drm_device_path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_drm_dumb_allocator_finalize (GObject * obj)
+{
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (obj);
+
+ if (alloc->dmabuf_alloc)
+ gst_object_unref (alloc->dmabuf_alloc);
+
+ g_free (alloc->drm_device_path);
+ alloc->drm_device_path = NULL;
+
+#ifdef HAVE_LIBDRM
+ if (alloc->drm_fd >= 0) {
+ close (alloc->drm_fd);
+ alloc->drm_fd = -1;
+ }
+#endif
+
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+gst_drm_dumb_allocator_class_init (GstDRMDumbAllocatorClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstAllocatorClass *allocator_class;
+
+ allocator_class = GST_ALLOCATOR_CLASS (klass);
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ allocator_class->free = gst_drm_dumb_allocator_free;
+
+ gobject_class->set_property = gst_drm_dumb_allocator_set_property;
+ gobject_class->get_property = gst_drm_dumb_allocator_get_property;
+ gobject_class->finalize = gst_drm_dumb_allocator_finalize;
+
+ /**
+ * GstDRMDumbAllocator:drm-fd:
+ *
+ * Since: 1.24
+ */
+ g_props[PROP_DRM_FD] = g_param_spec_int ("drm-fd", "DRM fd",
+ "DRM file descriptor", -1, G_MAXINT, -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ /**
+ * GstDRMDumbAllocator:drm-device-path:
+ *
+ * Since: 1.24
+ */
+ g_props[PROP_DRM_DEVICE_PATH] = g_param_spec_string ("drm-device-path",
+ "DRM device path", "DRM device path", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (gobject_class, PROP_N, g_props);
+}
+
+static gpointer
+gst_drm_dumb_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (mem->allocator);
+ GstDRMDumbMemory *drmmem;
+ gint err;
+ gpointer out;
+ struct drm_mode_map_dumb arg = { 0, };
+
+
+ if (!check_drm_fd (alloc))
+ return NULL;
+
+ drmmem = (GstDRMDumbMemory *) mem;
+ if (!drmmem->size)
+ return NULL;
+
+ /* Reuse existing buffer object mapping if possible */
+ if (drmmem->ptr != NULL) {
+ goto out;
+ }
+
+ arg.handle = drmmem->handle;
+
+ err = drmIoctl (alloc->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
+ if (err) {
+ GST_ERROR_OBJECT (alloc, "Failed to get offset of buffer object: %s %d",
+ g_strerror (errno), errno);
+ return NULL;
+ }
+
+ out = mmap (0, drmmem->size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, alloc->drm_fd, arg.offset);
+ if (out == MAP_FAILED) {
+ GST_ERROR_OBJECT (alloc, "Failed to map dumb buffer object: %s %d",
+ g_strerror (errno), errno);
+ return NULL;
+ }
+ drmmem->ptr = out;
+
+out:
+ g_atomic_int_inc (&drmmem->refs);
+ return drmmem->ptr;
+
+#else
+ return NULL;
+#endif
+}
+
+static void
+gst_drm_dumb_memory_unmap (GstMemory * mem)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbMemory *drmmem;
+
+ if (!check_drm_fd ((GstDRMDumbAllocator *) mem->allocator))
+ return;
+
+ drmmem = (GstDRMDumbMemory *) mem;
+ if (!drmmem->size)
+ return;
+
+ if (g_atomic_int_dec_and_test (&drmmem->refs)) {
+ munmap (drmmem->ptr, drmmem->size);
+ drmmem->ptr = NULL;
+ }
+#endif
+}
+
+static void
+gst_drm_dumb_allocator_init (GstDRMDumbAllocator * alloc)
+{
+ GstAllocator *base_alloc = GST_ALLOCATOR_CAST (alloc);
+
+ alloc->drm_fd = -1;
+ alloc->drm_device_path = NULL;
+
+ base_alloc->mem_type = GST_DRM_DUMB_MEMORY_TYPE;
+ base_alloc->mem_map = gst_drm_dumb_memory_map;
+ base_alloc->mem_unmap = gst_drm_dumb_memory_unmap;
+ /* Use the default, fallback copy function */
+
+ GST_OBJECT_FLAG_SET (alloc, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
+}
+
+#ifdef HAVE_LIBDRM
+static gboolean
+check_cap (GstDRMDumbAllocator * alloc)
+{
+ gint ret;
+ guint64 has_dumb = 0;
+
+ if (!alloc)
+ return FALSE;
+
+ if (!check_drm_fd (alloc))
+ return FALSE;
+
+ ret = drmGetCap (alloc->drm_fd, DRM_CAP_DUMB_BUFFER, &has_dumb);
+ if (ret)
+ GST_WARNING_OBJECT (alloc, "could not get dumb buffer capability");
+
+ return ! !(has_dumb);
+}
+#endif
+
+/**
+ * gst_drm_dumb_allocator_new_with_fd:
+ * @drm_fd: file descriptor of the DRM device
+ *
+ * Creates a new #GstDRMDumbAllocator for the specific file desciptor. This
+ * function can fail if the file descriptor is not a DRM device or if
+ * the DRM device does not support DUMB allocation.
+ *
+ * Returns: (transfer full) (nullable): a new DRM Dumb allocator. Use gst_object_unref()
+ * to release the allocator after usage.
+ *
+ * Since: 1.24
+
+ */
+GstAllocator *
+gst_drm_dumb_allocator_new_with_fd (gint drm_fd)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbAllocator *alloc;
+
+ alloc = g_object_new (GST_TYPE_DRM_DUMB_ALLOCATOR, "drm-fd", drm_fd, NULL);
+ gst_object_ref_sink (alloc);
+
+ if (!check_drm_fd (alloc))
+ g_clear_object (&alloc);
+
+ if (!check_cap (alloc))
+ g_clear_object (&alloc);
+
+ return alloc ? GST_ALLOCATOR (alloc) : NULL;
+
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * gst_drm_dumb_allocator_new_with_device_path:
+ * @drm_device_path: path to the DRM device to open
+ *
+ * Creates a new #GstDRMDumbAllocator for the specific device path. This
+ * function can fail if the path does not exist, is not a DRM device or if
+ * the DRM device doesnot support DUMB allocation.
+ *
+ * Returns: (transfer full) (nullable): a new DRM Dumb allocator. Use gst_object_unref()
+ * to release the allocator after usage.
+ *
+ * Since: 1.24
+ */
+GstAllocator *
+gst_drm_dumb_allocator_new_with_device_path (const gchar * drm_device_path)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbAllocator *alloc;
+
+ alloc = g_object_new (GST_TYPE_DRM_DUMB_ALLOCATOR,
+ "drm-device-path", drm_device_path, NULL);
+ gst_object_ref_sink (alloc);
+
+ if (!check_drm_fd (alloc))
+ g_clear_object (&alloc);
+
+ if (!check_cap (alloc))
+ g_clear_object (&alloc);
+
+ return alloc ? GST_ALLOCATOR (alloc) : NULL;
+
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * gst_drm_dumb_allocator_alloc:
+ * @allocator: the allocator instance
+ * @drm_fourcc: the DRM format to allocate for
+ * @width: padded width for this allocation
+ * @height: padded height for this allocation
+ * @out_pitch: (out): the pitch as returned by the driver
+ *
+ * Allocated a DRM buffer object for the specific @drm_fourcc, @width and
+ * @height. Note that the DRM Dumb allocation interface is agnostic to the
+ * pixel format. This @drm_fourcc is converted into a bpp (bit-per-pixel)
+ * number and the height is scaled according to the sub-sampling.
+ *
+ * Returns: (transfer full): a new DRM Dumb #GstMemory. Use gst_memory_unref()
+ * to release the memory after usage.
+ *
+ * Since: 1.24
+ */
+GstMemory *
+gst_drm_dumb_allocator_alloc (GstAllocator * base_alloc,
+ guint32 drm_fourcc, guint32 width, guint32 height, guint32 * out_pitch)
+{
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc);
+ GstDRMDumbMemory *drmmem;
+ GstMemory *mem;
+
+ drmmem = g_new0 (GstDRMDumbMemory, 1);
+ mem = GST_MEMORY_CAST (drmmem);
+
+ if (!gst_drm_dumb_allocator_memory_create (alloc, drmmem,
+ drm_fourcc, width, height, out_pitch)) {
+ g_free (drmmem);
+ return NULL;
+ }
+
+ gst_memory_init (mem, 0, base_alloc, NULL, drmmem->size, 0, 0, drmmem->size);
+ return mem;
+}
+
+/**
+ * gst_drm_dumb_allocator_has_prime_export:
+ * @allocator: the #GstAllocator
+ *
+ * This function allow verifying if the driver support dma-buf exportation.
+ *
+ * Returns: %TRUE if the allocator support exporting dma-buf.
+ *
+ * Since: 1.24
+ */
+gboolean
+gst_drm_dumb_allocator_has_prime_export (GstAllocator * base_alloc)
+{
+#ifdef HAVE_LIBDRM
+ GstDRMDumbAllocator *alloc = GST_DRM_DUMB_ALLOCATOR (base_alloc);
+ gint ret;
+ guint64 has_prime = 0;
+
+ if (!check_drm_fd (alloc))
+ return FALSE;
+
+ ret = drmGetCap (alloc->drm_fd, DRM_CAP_PRIME, &has_prime);
+ if (ret)
+ GST_WARNING_OBJECT (alloc, "could not get prime capability");
+
+ return ! !(has_prime & DRM_PRIME_CAP_EXPORT);
+
+#else
+ return FALSE;
+#endif
+}
diff --git a/gst-libs/gst/allocators/gstdrmdumb.h b/gst-libs/gst/allocators/gstdrmdumb.h
new file mode 100644
index 0000000..fa5f5ed
--- /dev/null
+++ b/gst-libs/gst/allocators/gstdrmdumb.h
@@ -0,0 +1,88 @@
+/* GStreamer
+ *
+ * Copyright (C) 2016 Igalia
+ * Copyright (C) 2023 Collabora
+ *
+ * Authors:
+ * Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
+ * Javier Martin <javiermartin@by.com.es>
+ * Colin Kinloch <colin.kinloch@collabora.com>
+ *
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+#include <gst/allocators/allocators-prelude.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstDRMDumbAllocator:
+ *
+ * Private intance object for #GstDRMDumbAllocator.
+ *
+ * Since: 1.24
+ */
+
+/**
+ * GstDRMDumbAllocatorClass.parent_class:
+ *
+ * Parent Class.
+ *
+ * Since: 1.24
+ */
+
+/**
+ * GST_TYPE_DRM_DUMB_ALLOCATOR:
+ *
+ * Macro that returns the #GstDRMDumbAllocator type.
+ *
+ * Since: 1.24
+ */
+#define GST_TYPE_DRM_DUMB_ALLOCATOR gst_drm_dumb_allocator_get_type ()
+GST_ALLOCATORS_API
+G_DECLARE_FINAL_TYPE (GstDRMDumbAllocator, gst_drm_dumb_allocator,
+ GST, DRM_DUMB_ALLOCATOR, GstAllocator);
+
+GST_ALLOCATORS_API
+gboolean gst_is_drm_dumb_memory (GstMemory * mem);
+
+GST_ALLOCATORS_API
+guint32 gst_drm_dumb_memory_get_handle (GstMemory * mem);
+
+GST_ALLOCATORS_API
+GstMemory* gst_drm_dumb_memory_export_dmabuf (GstMemory * mem);
+
+GST_ALLOCATORS_API
+GstAllocator * gst_drm_dumb_allocator_new_with_fd (gint drm_fd);
+
+GST_ALLOCATORS_API
+GstAllocator * gst_drm_dumb_allocator_new_with_device_path (const gchar *drm_device_path);
+
+GST_ALLOCATORS_API
+GstMemory * gst_drm_dumb_allocator_alloc (GstAllocator * allocator,
+ guint32 drm_fourcc,
+ guint32 width,
+ guint32 height,
+ guint32 *out_pitch);
+
+GST_ALLOCATORS_API
+gboolean gst_drm_dumb_allocator_has_prime_export (GstAllocator * allocator);
+
+G_END_DECLS
diff --git a/gst-libs/gst/allocators/meson.build b/gst-libs/gst/allocators/meson.build
index 6ee0bfd..879c2fc 100644
--- a/gst-libs/gst/allocators/meson.build
+++ b/gst-libs/gst/allocators/meson.build
@@ -4,10 +4,11 @@ gst_allocators_headers = files([
'gstfdmemory.h',
'gstphysmemory.h',
'gstdmabuf.h',
+ 'gstdrmdumb.h',
])
install_headers(gst_allocators_headers, subdir : 'gstreamer-1.0/gst/allocators/')
-gst_allocators_sources = files([ 'gstdmabuf.c', 'gstfdmemory.c', 'gstphysmemory.c'])
+gst_allocators_sources = files([ 'gstdrmdumb.c', 'gstdmabuf.c', 'gstfdmemory.c', 'gstphysmemory.c'])
gstallocators = library('gstallocators-@0@'.format(api_version),
gst_allocators_sources,
c_args : gst_plugins_base_args + ['-DBUILDING_GST_ALLOCATORS', '-DG_LOG_DOMAIN="GStreamer-Allocators"'],
@@ -16,12 +17,12 @@ gstallocators = library('gstallocators-@0@'.format(api_version),
soversion : soversion,
darwin_versions : osxversion,
install : true,
- dependencies : [gst_dep],
+ dependencies : [libdrm_dep, gst_dep],
)
pkg_name = 'gstreamer-allocators-1.0'
pkgconfig.generate(gstallocators,
- libraries : [gst_dep],
+ libraries : [libdrm_dep, gst_dep],
variables : pkgconfig_variables,
subdirs : pkgconfig_subdirs,
name : pkg_name,
diff --git a/meson.build b/meson.build
index 19a8347..699af91 100644
--- a/meson.build
+++ b/meson.build
@@ -305,6 +305,13 @@ if get_option('default_library') == 'static'
gst_plugins_base_args += ['-DGST_STATIC_COMPILATION']
endif
+libdrm_dep = dependency('libdrm', version : '>= 2.4.98',
+ required : get_option('drm'),
+ fallback: ['libdrm', 'ext_libdrm']
+)
+
+core_conf.set('HAVE_LIBDRM', libdrm_dep.found())
+
# X11 checks are for sys/ and tests/
x11_dep = dependency('x11', required : get_option('x11'))
# GIO is used by the GIO plugin, and by the TCP, SDP, and RTSP plugins
diff --git a/meson_options.txt b/meson_options.txt
index 50ec6aa..0db1840 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -36,6 +36,7 @@ option('audiorate', type : 'feature', value : 'auto')
option('audioresample', type : 'feature', value : 'auto')
option('audiotestsrc', type : 'feature', value : 'auto')
option('compositor', type : 'feature', value : 'auto')
+option('drm', type : 'feature', value : 'auto')
option('encoding', type : 'feature', value : 'auto')
option('gio', type : 'feature', value : 'auto')
option('gio-typefinder', type : 'feature', value : 'auto')
--
2.25.1

View File

@@ -0,0 +1,28 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}/:"
SRC_URI:append = " \
file://0001-allocators-Add-a-DRM-Dumb-Allocator.patch \
"
PACKAGECONFIG ?= " \
${GSTREAMER_ORC} \
${PACKAGECONFIG_GL} \
${@bb.utils.filter('DISTRO_FEATURES', 'alsa x11', d)} \
jpeg ogg pango png theora vorbis \
${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland egl', '', d)} \
encoding \
"
PACKAGECONFIG[encoding] = "-Dencoding=enabled,-Dencoding=disabled,"
#enable hardware convert/scale in playbin (gstsubtitleoverlay.c, gstplaysinkvideoconvert.c, gstplaysink.c) & gstencodebin (gstencodebin.c)
#disable software convert/scale/rate in gstencodebin (gstencodebin.c)
#HW_TRANSFORM_CONFIG = 'CFLAGS="-DCOLORSPACE=\\\\\\"autovideoconvert\\\\\\" \
# -DCOLORSPACE_SUBT=\\\\\\"videoconvert\\\\\\" \
# -DGST_PLAYBIN_DEFAULT_FLAGS=0x00000017 \
# -DCOLORSPACE2=\\\\\\"identity\\\\\\" \
# -DVIDEOSCALE=\\\\\\"identity\\\\\\" \
# -DVIDEORATE=\\\\\\"identity\\\\\\" "'
#CACHED_CONFIGUREVARS += "${@bb.utils.contains('DISTRO_FEATURES', 'hwdecode', '${HW_TRANSFORM_CONFIG}', '', d)}"

View File

@@ -0,0 +1,18 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}/:"
PACKAGECONFIG ?= " \
${GSTREAMER_ORC} \
${PACKAGECONFIG_SOUP} \
${@bb.utils.filter('DISTRO_FEATURES', 'pulseaudio x11', d)} \
bz2 cairo flac gdk-pixbuf gudev jpeg lame libpng mpg123 speex taglib v4l2 \
libv4l2 \
${@bb.utils.contains_any('DISTRO_FEATURES', '${GTK3DISTROFEATURES}', 'gtk', '', d)} \
"
EXTRA_OEMESON += " \
-Dv4l2-probe=enabled \
-Dv4l2-libv4l2=enabled \
"
# remove qt5 for the moment
PACKAGECONFIG:remove = " qt5"

View File

@@ -0,0 +1,68 @@
From c590304b77a723b9b69f3d2d2f777dc27ac4901c Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Wed, 15 Feb 2023 13:10:25 -0500
Subject: [PATCH] basesink: Add GST_BASE_SINK_FLOW_DROPPED return value
This new flow return value can be used in ::render virtual method
to signal that a frame is not being rendered.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3801>
---
libs/gst/base/gstbasesink.c | 8 ++++++++
libs/gst/base/gstbasesink.h | 14 ++++++++++++++
2 files changed, 22 insertions(+)
diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c
index 294d946..a3abcc7 100644
--- a/libs/gst/base/gstbasesink.c
+++ b/libs/gst/base/gstbasesink.c
@@ -3950,6 +3950,11 @@ again:
if (bclass->render)
ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
+
+ if (ret == GST_BASE_SINK_FLOW_DROPPED) {
+ ret = GST_FLOW_OK;
+ goto dropped;
+ }
} else {
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
@@ -3959,6 +3964,9 @@ again:
/* Set the first buffer and buffer list to be included in last sample */
gst_base_sink_set_last_buffer (basesink, sync_buf);
gst_base_sink_set_last_buffer_list (basesink, buffer_list);
+
+ /* Not currently supported */
+ g_assert (ret != GST_BASE_SINK_FLOW_DROPPED);
}
if (ret == GST_FLOW_STEP)
diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h
index 8edae03..2cf799a 100644
--- a/libs/gst/base/gstbasesink.h
+++ b/libs/gst/base/gstbasesink.h
@@ -58,6 +58,20 @@ G_BEGIN_DECLS
#define GST_BASE_SINK_PREROLL_SIGNAL(obj) g_cond_signal (GST_BASE_SINK_GET_PREROLL_COND (obj));
#define GST_BASE_SINK_PREROLL_BROADCAST(obj) g_cond_broadcast (GST_BASE_SINK_GET_PREROLL_COND (obj));
+/**
+ * GST_BASE_SINK_FLOW_DROPPED:
+ *
+ * A #GstFlowReturn that can be returned from
+ * #GstBaseSinkClass::render to indicate that the output buffer was not
+ * rendered.
+ *
+ * Note that this is currently not support for #GstBaseSinkClass::render_list
+ * virtual method.
+ *
+ * Since: 1.24
+ */
+#define GST_BASE_SINK_FLOW_DROPPED GST_FLOW_CUSTOM_SUCCESS
+
typedef struct _GstBaseSink GstBaseSink;
typedef struct _GstBaseSinkClass GstBaseSinkClass;
typedef struct _GstBaseSinkPrivate GstBaseSinkPrivate;
--
2.25.1

View File

@@ -0,0 +1,5 @@
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
SRC_URI:append = " \
file://0001-basesink-Add-GST_BASE_SINK_FLOW_DROPPED-return-value.patch \
"