From 2af4bc8562f970207e3b1f6fc0b377ecf116a69c Mon Sep 17 00:00:00 2001
From: Tushar Darote <tdarote@qti.qualcomm.com>
Date: Mon, 8 Jun 2026 18:05:51 +0530
Subject: [PATCH] wayland: handle padded buffers in wl_shm buffer creation

Use GstBuffer metadata when constructing wl_shm buffers so stride and
size reflect the actual buffer layout. This fixes incorrect rendering
with DMA-backed buffers that include padding or custom strides.

gst_wl_shm_memory_construct_wl_buffer() now takes a GstBuffer instead of
a GstMemory. It derives the video layout from the buffer's GstVideoMeta
when present (width, height, stride and offset), falling back to the
caps-derived GstVideoInfo otherwise, and computes the real memory
footprint (plane_offset + stride * height) rather than the unpadded
GstVideoInfo size. The single-memory and fd-memory checks that used to
live in the callers are now performed inside the function.

All callers in gstwlwindow, waylandsink and gtkwaylandsink are updated
accordingly.

Upstream-Status: Backport [adapted from https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/11370 due to API differences]

Signed-off-by: Tushar Darote <tdarote@qti.qualcomm.com>
---
 ext/gtk/gstgtkwaylandsink.c              | 11 ++---
 ext/wayland/gstwaylandsink.c             | 11 ++---
 gst-libs/gst/wayland/gstwlshmallocator.c | 59 ++++++++++++++++++------
 gst-libs/gst/wayland/gstwlshmallocator.h |  2 +-
 gst-libs/gst/wayland/gstwlwindow.c       |  4 +-
 5 files changed, 52 insertions(+), 35 deletions(-)

diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
index 0660b30..81bcd7f 100644
--- a/ext/gtk/gstgtkwaylandsink.c
+++ b/ext/gtk/gstgtkwaylandsink.c
@@ -1179,7 +1179,6 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
       gst_gtk_wayland_sink_get_instance_private (self);
   GstBuffer *to_render;
   GstWlBuffer *wlbuffer;
-  GstMemory *mem;
   struct wl_buffer *wbuf = NULL;
 
   GstFlowReturn ret = GST_FLOW_OK;
@@ -1240,8 +1239,6 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
   }
 
   /* update video info from video meta */
-  mem = gst_buffer_peek_memory (buffer, 0);
-
   GST_LOG_OBJECT (self,
       "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our "
       "display, creating it", buffer);
@@ -1301,9 +1298,8 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
 handle_shm:
   if (!wbuf && gst_wl_display_check_format_for_shm (priv->display,
           &priv->render_info)) {
-    if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
-      wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,
-          &priv->render_info);
+    wbuf = gst_wl_shm_memory_construct_wl_buffer (buffer, priv->display,
+        &priv->render_info);
 
     /* If nothing worked, copy into our internal pool */
     if (!wbuf) {
@@ -1328,8 +1324,7 @@ handle_shm:
 
       /* attach a wl_buffer if there isn't one yet */
       if (G_UNLIKELY (!wlbuffer)) {
-        mem = gst_buffer_peek_memory (to_render, 0);
-        wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display,
+        wbuf = gst_wl_shm_memory_construct_wl_buffer (to_render, priv->display,
             &priv->render_info);
 
         if (G_UNLIKELY (!wbuf))
diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c
index 443db24..60aa11d 100644
--- a/ext/wayland/gstwaylandsink.c
+++ b/ext/wayland/gstwaylandsink.c
@@ -1011,7 +1011,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
   GstWaylandSink *self = GST_WAYLAND_SINK (vsink);
   GstBuffer *to_render;
   GstWlBuffer *wlbuffer;
-  GstMemory *mem;
   struct wl_buffer *wbuf = NULL;
 
   GstFlowReturn ret = GST_FLOW_OK;
@@ -1086,8 +1085,6 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
   }
 
   /* update video info from video meta */
-  mem = gst_buffer_peek_memory (buffer, 0);
-
   GST_LOG_OBJECT (self,
       "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our "
       "display, creating it", buffer);
@@ -1147,9 +1144,8 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
 handle_shm:
   if (!wbuf && gst_wl_display_check_format_for_shm (self->display,
           &self->render_info)) {
-    if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem))
-      wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display,
-          &self->video_info);
+    wbuf = gst_wl_shm_memory_construct_wl_buffer (buffer, self->display,
+        &self->video_info);
 
     /* If nothing worked, copy into our internal pool */
     if (!wbuf) {
@@ -1173,8 +1169,7 @@ handle_shm:
 
       /* attach a wl_buffer if there isn't one yet */
       if (G_UNLIKELY (!wlbuffer)) {
-        mem = gst_buffer_peek_memory (to_render, 0);
-        wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, self->display,
+        wbuf = gst_wl_shm_memory_construct_wl_buffer (to_render, self->display,
             &self->video_info);
 
         if (G_UNLIKELY (!wbuf))
diff --git a/gst-libs/gst/wayland/gstwlshmallocator.c b/gst-libs/gst/wayland/gstwlshmallocator.c
index 671f2b7..11fb22c 100644
--- a/gst-libs/gst/wayland/gstwlshmallocator.c
+++ b/gst-libs/gst/wayland/gstwlshmallocator.c
@@ -76,32 +76,60 @@ gst_wl_shm_validate_video_info (const GstVideoInfo * vinfo)
 }
 
 struct wl_buffer *
-gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem, GstWlDisplay * display,
-    const GstVideoInfo * info)
+gst_wl_shm_memory_construct_wl_buffer (GstBuffer * buf,
+    GstWlDisplay * display, const GstVideoInfo * info)
 {
+  GstMemory *mem;
+  GstVideoMeta *vmeta;
+  GstVideoInfo vinfo;
   gint width, height, stride;
-  gsize offset, size, memsize, maxsize;
+  gsize plane_offset = 0;
+  gsize mem_offset = 0;
+  gsize size, memsize, maxsize;
   enum wl_shm_format format;
   struct wl_shm_pool *wl_pool;
   struct wl_buffer *wbuffer;
 
-  if (!gst_wl_shm_validate_video_info (info)) {
+  /* Ensure the buffer has exactly one memory block */
+  if (gst_buffer_n_memory (buf) != 1)
+    return NULL;
+
+  /* Retrieve the memory and ensure it is FD backed */
+  mem = gst_buffer_peek_memory (buf, 0);
+  if (!gst_is_fd_memory (mem))
+    return NULL;
+
+  /* Start from caps-derived video info */
+  vinfo = *info;
+
+  /* Override layout using GstVideoMeta if present */
+  vmeta = gst_buffer_get_video_meta (buf);
+  if (vmeta) {
+    vinfo.width = vmeta->width;
+    vinfo.height = vmeta->height;
+    vinfo.stride[0] = vmeta->stride[0];
+    vinfo.offset[0] = vmeta->offset[0];
+  }
+
+  /* Validate the final video layout */
+  if (!gst_wl_shm_validate_video_info (&vinfo)) {
     GST_DEBUG_OBJECT (display, "Unsupported strides and offsets.");
     return NULL;
   }
 
-  width = GST_VIDEO_INFO_WIDTH (info);
-  height = GST_VIDEO_INFO_HEIGHT (info);
-  stride = GST_VIDEO_INFO_PLANE_STRIDE (info, 0);
-  size = GST_VIDEO_INFO_SIZE (info);
-  format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (info));
+  width = GST_VIDEO_INFO_WIDTH (&vinfo);
+  height = GST_VIDEO_INFO_HEIGHT (&vinfo);
+  stride = GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, 0);
+  plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (&vinfo, 0);
+
+  /* wl_shm requires the actual memory footprint of the buffer */
+  size = plane_offset + (stride * height);
+  format = gst_video_format_to_wl_shm_format (GST_VIDEO_INFO_FORMAT (&vinfo));
 
-  memsize = gst_memory_get_sizes (mem, &offset, &maxsize);
-  offset += GST_VIDEO_INFO_PLANE_OFFSET (info, 0);
+  memsize = gst_memory_get_sizes (mem, &mem_offset, &maxsize);
 
-  g_return_val_if_fail (gst_is_fd_memory (mem), NULL);
   g_return_val_if_fail (size <= memsize, NULL);
-  g_return_val_if_fail (gst_wl_display_check_format_for_shm (display, info),
+  g_return_val_if_fail (gst_wl_display_check_format_for_shm (display, &vinfo),
       NULL);
 
   GST_DEBUG_OBJECT (display, "Creating wl_buffer from SHM of size %"
@@ -110,8 +138,9 @@ gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem, GstWlDisplay * display,
 
   wl_pool = wl_shm_create_pool (gst_wl_display_get_shm (display),
       gst_fd_memory_get_fd (mem), memsize);
-  wbuffer = wl_shm_pool_create_buffer (wl_pool, offset, width, height, stride,
-      format);
+  wbuffer =
+      wl_shm_pool_create_buffer (wl_pool, mem_offset + plane_offset, width,
+      height, stride, format);
   wl_shm_pool_destroy (wl_pool);
 
   return wbuffer;
diff --git a/gst-libs/gst/wayland/gstwlshmallocator.h b/gst-libs/gst/wayland/gstwlshmallocator.h
index 4309bd7..38f3ba5 100644
--- a/gst-libs/gst/wayland/gstwlshmallocator.h
+++ b/gst-libs/gst/wayland/gstwlshmallocator.h
@@ -33,7 +33,7 @@ GST_WL_API
 void gst_wl_shm_init_once (void);
 
 GST_WL_API
-struct wl_buffer * gst_wl_shm_memory_construct_wl_buffer (GstMemory * mem,
+struct wl_buffer * gst_wl_shm_memory_construct_wl_buffer (GstBuffer * buf,
     GstWlDisplay * display, const GstVideoInfo * info);
 
 G_END_DECLS
diff --git a/gst-libs/gst/wayland/gstwlwindow.c b/gst-libs/gst/wayland/gstwlwindow.c
index 3c5c3cc..cd9c964 100644
--- a/gst-libs/gst/wayland/gstwlwindow.c
+++ b/gst-libs/gst/wayland/gstwlwindow.c
@@ -985,9 +985,7 @@ gst_wl_window_update_borders (GstWlWindow * self)
     buf = gst_buffer_new_allocate (alloc, info.size, NULL);
     gst_buffer_memset (buf, 0, 0, info.size);
 
-    wlbuf =
-        gst_wl_shm_memory_construct_wl_buffer (gst_buffer_peek_memory (buf, 0),
-        priv->display, &info);
+    wlbuf = gst_wl_shm_memory_construct_wl_buffer (buf, priv->display, &info);
 
     g_object_unref (alloc);
   }
-- 
2.34.1

