diff --git a/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.c b/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.c
new file mode 100644
index 0000000000..7eb6f94726
--- /dev/null
+++ b/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.c
@@ -0,0 +1,422 @@
+/*
+ * GStreamer
+ * Copyright © 2024 Advanced Micro Devices, Inc.
+ *
+ * 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 <ext/gl/gstgldmabufbufferpool.h>
+
+#include <gst/allocators/gstdmabuf.h>
+#include <gst/gl/gstglcontext.h>
+#include <gst/gl/gstglfuncs.h>
+#include <gst/gl/gstglmemory.h>
+#include <gst/gl/gstglsyncmeta.h>
+#include <gst/gl/egl/gsteglimage.h>
+
+#define GST_GL_DMABUF_EGLIMAGE "gst.gl.dmabuf.eglimage"
+
+typedef struct _GstGLDMABufBufferPoolPrivate
+{
+  GstBufferPool *dmabuf_pool;
+  GstGLMemoryAllocator *allocator;
+  GstGLVideoAllocationParams *glparams;
+
+  gboolean add_glsyncmeta;
+} GstGLDMABufBufferPoolPrivate;
+
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_DMABUF_BUFFER_POOL);
+#define GST_CAT_DEFAULT GST_CAT_GL_DMABUF_BUFFER_POOL
+
+#define gst_gl_dmabuf_buffer_pool_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstGLDMABufBufferPool, gst_gl_dmabuf_buffer_pool,
+    GST_TYPE_GL_BUFFER_POOL, G_ADD_PRIVATE (GstGLDMABufBufferPool)
+    GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_DMABUF_BUFFER_POOL,
+        "gldmabufbufferpool", 0, "GL-DMABuf Buffer Pool"));
+
+static gboolean
+gst_gl_dmabuf_buffer_pool_set_config (GstBufferPool * pool,
+    GstStructure * config)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (pool);
+
+  GstAllocator *allocator = NULL;
+  GstAllocationParams alloc_params;
+  GstGLAllocationParams *glparams;
+  GstVideoAlignment video_align = { 0 };
+  GstCaps *caps;
+  guint size;
+  guint min;
+  guint max;
+  guint i;
+
+  if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min, &max)) {
+    goto wrong_config;
+  }
+
+  if (!gst_buffer_pool_config_get_allocator (config, &allocator, &alloc_params)) {
+    goto wrong_config;
+  }
+
+  gst_clear_object (&self->priv->allocator);
+
+  if (allocator) {
+    if (!GST_IS_GL_MEMORY_ALLOCATOR (allocator)) {
+      gst_clear_object (&allocator);
+      goto wrong_allocator;
+    } else {
+      self->priv->allocator = gst_object_ref (allocator);
+    }
+  } else {
+    self->priv->allocator =
+        gst_gl_memory_allocator_get_default (GST_GL_BUFFER_POOL
+        (pool)->context);
+  }
+
+  /*
+   * This alignment is needed by nearly all AMD GPUs and should work fine on
+   * Mali as well. To my knowledge there is no API to query it at runtime, so
+   * it has to be hardcoded here. Users of the pool can still override the
+   * values with GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT added to the config.
+   */
+  for (i = 0; i != GST_VIDEO_MAX_PLANES; ++i) {
+    video_align.stride_align[i] = 256 - 1;
+  }
+
+  if (!gst_buffer_pool_config_has_option (config,
+          GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
+    gst_buffer_pool_config_add_option (config,
+        GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+    gst_buffer_pool_config_set_video_alignment (config, &video_align);
+  }
+
+  gst_buffer_pool_config_get_video_alignment (config, &video_align);
+  alloc_params.align = MAX (alloc_params.align, video_align.stride_align[0]);
+
+  gst_buffer_pool_config_set_allocator (config, allocator, &alloc_params);
+
+  glparams = gst_buffer_pool_config_get_gl_allocation_params (config);
+  if (glparams) {
+    g_clear_pointer (&glparams->alloc_params, gst_allocation_params_free);
+    glparams->alloc_params = gst_allocation_params_copy (&alloc_params);
+    gst_buffer_pool_config_set_gl_allocation_params (config, glparams);
+    g_clear_pointer (&glparams, gst_gl_allocation_params_free);
+  }
+
+  self->priv->add_glsyncmeta = gst_buffer_pool_config_has_option (config,
+      GST_BUFFER_POOL_OPTION_GL_SYNC_META);
+
+  if (!GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config)) {
+    return FALSE;
+  }
+
+  g_clear_pointer ((GstGLAllocationParams **) & self->priv->glparams,
+      gst_gl_allocation_params_free);
+  self->priv->glparams = (GstGLVideoAllocationParams *)
+      gst_gl_buffer_pool_get_gl_allocation_params (GST_GL_BUFFER_POOL (pool));
+
+  self->priv->glparams->parent.alloc_flags |=
+      GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE;
+
+  /* Now configure the dma-buf pool. */
+
+  config = gst_buffer_pool_get_config (self->priv->dmabuf_pool);
+  gst_buffer_pool_config_set_params (config, caps, size, min, max);
+  gst_buffer_pool_config_add_option (config,
+      GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
+  gst_buffer_pool_config_set_video_alignment (config, &video_align);
+
+  return gst_buffer_pool_set_config (self->priv->dmabuf_pool, config);
+
+wrong_config:
+  {
+    GST_WARNING_OBJECT (pool, "Incorrect config for this pool");
+    return FALSE;
+  }
+wrong_allocator:
+  {
+    GST_WARNING_OBJECT (pool, "Incorrect allocator type for this pool");
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_gl_dmabuf_buffer_pool_start (GstBufferPool * pool)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (pool);
+
+  if (!gst_buffer_pool_set_active (self->priv->dmabuf_pool, TRUE)) {
+    return FALSE;
+  }
+
+  return GST_BUFFER_POOL_CLASS (parent_class)->start (pool);
+}
+
+static gboolean
+gst_gl_dmabuf_buffer_pool_stop (GstBufferPool * pool)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (pool);
+
+  if (!gst_buffer_pool_set_active (self->priv->dmabuf_pool, FALSE)) {
+    return FALSE;
+  }
+
+  return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool);
+}
+
+typedef struct
+{
+  GstEGLImage *eglimage[GST_VIDEO_MAX_PLANES];
+  gpointer gpuhandle[GST_VIDEO_MAX_PLANES];
+  guint n_planes;
+} WrapDMABufData;
+
+static void
+_wrap_dmabuf_eglimage (GstGLContext * context, gpointer data)
+{
+  WrapDMABufData *d = data;
+  const GstGLFuncs *gl = context->gl_vtable;
+  GLuint tex_ids[GST_VIDEO_MAX_PLANES];
+  guint i;
+
+  gl->GenTextures (d->n_planes, tex_ids);
+
+  for (i = 0; i < d->n_planes; ++i) {
+    gl->BindTexture (GL_TEXTURE_2D, tex_ids[i]);
+    gl->EGLImageTargetTexture2D (GL_TEXTURE_2D,
+        gst_egl_image_get_image (d->eglimage[i]));
+
+    d->gpuhandle[i] = GUINT_TO_POINTER (tex_ids[i]);
+  }
+}
+
+static GstFlowReturn
+gst_gl_dmabuf_buffer_pool_alloc_buffer (GstBufferPool * pool,
+    GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (pool);
+  GstGLBufferPool *glpool = GST_GL_BUFFER_POOL (pool);
+
+  GstBuffer *buf;
+
+  if (!(buf = gst_buffer_new ())) {
+    goto no_buffer;
+  }
+
+  if (self->priv->add_glsyncmeta) {
+    gst_buffer_add_gl_sync_meta (glpool->context, buf);
+  }
+
+  *buffer = buf;
+
+  return GST_FLOW_OK;
+
+  /* ERROR */
+no_buffer:
+  {
+    GST_WARNING_OBJECT (self, "Could not create DMABuf buffer");
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
+gst_gl_dmabuf_buffer_pool_acquire_buffer (GstBufferPool * pool,
+    GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (pool);
+  GstGLBufferPool *glpool = GST_GL_BUFFER_POOL (pool);
+
+  GstVideoInfo *v_info = self->priv->glparams->v_info;
+  GstFlowReturn ret;
+  GstBuffer *dmabuf;
+  GstBuffer *buf;
+  WrapDMABufData data;
+  guint i;
+
+  ret = gst_buffer_pool_acquire_buffer (self->priv->dmabuf_pool, &dmabuf, NULL);
+  if (ret != GST_FLOW_OK) {
+    goto no_buffer;
+  }
+
+  data.n_planes = GST_VIDEO_INFO_N_PLANES (v_info);
+
+  for (i = 0; i < data.n_planes; ++i) {
+    guint mem_idx, length;
+    gsize skip;
+    GstMemory *dmabufmem;
+
+    if (!gst_buffer_find_memory (dmabuf, GST_VIDEO_INFO_PLANE_OFFSET (v_info,
+                i), 1, &mem_idx, &length, &skip)) {
+      GST_WARNING_OBJECT (self, "Could not find memory for plane %d", i);
+      return GST_FLOW_ERROR;
+    }
+
+    dmabufmem = gst_buffer_peek_memory (dmabuf, mem_idx);
+
+    g_assert (gst_is_dmabuf_memory (dmabufmem));
+
+    data.eglimage[i] = gst_egl_image_from_dmabuf (glpool->context,
+        gst_dmabuf_memory_get_fd (dmabufmem), v_info, i, skip);
+  }
+
+  gst_gl_context_thread_add (glpool->context, _wrap_dmabuf_eglimage, &data);
+
+  ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (pool, &buf,
+      params);
+  if (ret != GST_FLOW_OK) {
+    return GST_FLOW_ERROR;
+  }
+
+  if (!gst_gl_memory_setup_buffer (self->priv->allocator, buf,
+          self->priv->glparams, NULL, data.gpuhandle, data.n_planes)) {
+    goto mem_create_failed;
+  }
+
+  for (i = 0; i < data.n_planes; ++i) {
+    GstMemory *mem = gst_buffer_peek_memory (buf, i);
+
+    /* Unset wrapped flag, we want the texture be freed with the memory. */
+    GST_GL_MEMORY_CAST (mem)->texture_wrapped = FALSE;
+
+    gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
+        g_quark_from_static_string (GST_GL_DMABUF_EGLIMAGE),
+        data.eglimage[i], (GDestroyNotify) gst_egl_image_unref);
+  }
+
+  gst_buffer_add_parent_buffer_meta (buf, dmabuf);
+  gst_clear_buffer (&dmabuf);
+
+  *buffer = buf;
+
+  return GST_FLOW_OK;
+
+  /* ERROR */
+no_buffer:
+  {
+    GST_WARNING_OBJECT (self, "Could not create DMABuf buffer");
+    return GST_FLOW_ERROR;
+  }
+mem_create_failed:
+  {
+    GST_WARNING_OBJECT (self, "Could not create GL Memory");
+    return GST_FLOW_ERROR;
+  }
+}
+
+static void
+gst_gl_dmabuf_buffer_pool_reset_buffer (GstBufferPool * pool,
+    GstBuffer * buffer)
+{
+  gst_buffer_remove_all_memory (buffer);
+
+  GST_BUFFER_POOL_CLASS (parent_class)->reset_buffer (pool, buffer);
+}
+
+gboolean
+gst_is_gl_dmabuf_buffer (GstBuffer * buffer)
+{
+  return GST_IS_GL_DMABUF_BUFFER_POOL (buffer->pool);
+}
+
+GstBuffer *
+gst_gl_dmabuf_buffer_unwrap (GstBuffer * buffer)
+{
+  GstGLDMABufBufferPool *pool;
+  GstParentBufferMeta *meta;
+  GstBuffer *wrapped_dmabuf = NULL;
+
+  g_return_val_if_fail (gst_is_gl_dmabuf_buffer (buffer), NULL);
+
+  pool = GST_GL_DMABUF_BUFFER_POOL (buffer->pool);
+
+  if (gst_buffer_peek_memory (buffer, 0)->allocator !=
+      GST_ALLOCATOR (pool->priv->allocator)) {
+    return NULL;
+  }
+
+  meta = gst_buffer_get_parent_buffer_meta (buffer);
+
+  if (meta && meta->buffer) {
+    wrapped_dmabuf = gst_buffer_ref (meta->buffer);
+
+    gst_buffer_remove_meta (buffer, (GstMeta *) meta);
+
+    gst_buffer_copy_into (wrapped_dmabuf, buffer, GST_BUFFER_COPY_FLAGS |
+        GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META, 0, -1);
+  }
+
+  return wrapped_dmabuf;
+}
+
+GstBufferPool *
+gst_gl_dmabuf_buffer_pool_new (GstGLContext * context,
+    GstBufferPool * dmabuf_pool)
+{
+  GstGLDMABufBufferPool *pool;
+
+  pool = g_object_new (GST_TYPE_GL_DMABUF_BUFFER_POOL, NULL);
+  gst_object_ref_sink (pool);
+
+  GST_GL_BUFFER_POOL (pool)->context = gst_object_ref (context);
+
+  pool->priv->dmabuf_pool = gst_object_ref (dmabuf_pool);
+
+  GST_LOG_OBJECT (pool, "new GL-DMABuf buffer pool for pool %" GST_PTR_FORMAT
+      " and context %" GST_PTR_FORMAT, dmabuf_pool, context);
+
+  return GST_BUFFER_POOL_CAST (pool);
+}
+
+static void
+gst_gl_dmabuf_buffer_pool_init (GstGLDMABufBufferPool * pool)
+{
+  pool->priv = gst_gl_dmabuf_buffer_pool_get_instance_private (pool);
+}
+
+static void
+gst_gl_dmabuf_buffer_pool_finalize (GObject * object)
+{
+  GstGLDMABufBufferPool *self = GST_GL_DMABUF_BUFFER_POOL (object);
+
+  GST_LOG_OBJECT (self, "finalize GL-DMABuf buffer pool");
+
+  gst_clear_object (&self->priv->dmabuf_pool);
+  g_clear_pointer ((GstGLAllocationParams **) & self->priv->glparams,
+      gst_gl_allocation_params_free);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_gl_dmabuf_buffer_pool_class_init (GstGLDMABufBufferPoolClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
+
+  gobject_class->finalize = gst_gl_dmabuf_buffer_pool_finalize;
+
+  gstbufferpool_class->set_config = gst_gl_dmabuf_buffer_pool_set_config;
+  gstbufferpool_class->alloc_buffer = gst_gl_dmabuf_buffer_pool_alloc_buffer;
+  gstbufferpool_class->acquire_buffer =
+      gst_gl_dmabuf_buffer_pool_acquire_buffer;
+  gstbufferpool_class->reset_buffer = gst_gl_dmabuf_buffer_pool_reset_buffer;
+  gstbufferpool_class->start = gst_gl_dmabuf_buffer_pool_start;
+  gstbufferpool_class->stop = gst_gl_dmabuf_buffer_pool_stop;
+}
diff --git a/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.h b/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.h
new file mode 100644
index 0000000000..7c75872eea
--- /dev/null
+++ b/subprojects/gst-plugins-base/ext/gl/gstgldmabufbufferpool.h
@@ -0,0 +1,61 @@
+/*
+ * GStreamer
+ * Copyright © 2024 Advanced Micro Devices, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _GST_GL_DMABUF_BUFFER_POOL_H_
+#define _GST_GL_DMABUF_BUFFER_POOL_H_
+
+#include <gst/gl/gstglbufferpool.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstGLDMABufBufferPoolPrivate GstGLDMABufBufferPoolPrivate;
+
+/**
+ * GST_TYPE_GL_DMABUF_BUFFER_POOL:
+ *
+ * Since: 1.26
+ */
+#define GST_TYPE_GL_DMABUF_BUFFER_POOL (gst_gl_dmabuf_buffer_pool_get_type())
+G_DECLARE_FINAL_TYPE (GstGLDMABufBufferPool, gst_gl_dmabuf_buffer_pool, GST, GL_DMABUF_BUFFER_POOL, GstGLBufferPool)
+
+/**
+ * GstGLDMABufBufferPool:
+ *
+ * Opaque GstGLDMABufBufferPool struct
+ *
+ * Since: 1.26
+ */
+struct _GstGLDMABufBufferPool
+{
+  GstGLBufferPool parent;
+
+  /*< private >*/
+  GstGLDMABufBufferPoolPrivate *priv;
+};
+
+GstBufferPool *gst_gl_dmabuf_buffer_pool_new (GstGLContext * context, GstBufferPool * dmabuf_pool);
+
+gboolean gst_is_gl_dmabuf_buffer (GstBuffer * buffer);
+
+GstBuffer *gst_gl_dmabuf_buffer_unwrap (GstBuffer * buffer);
+
+G_END_DECLS
+
+#endif /* _GST_GL_DMABUF_BUFFER_POOL_H_ */
diff --git a/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.c b/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.c
index 1174f2ff64..c14dbae2ed 100644
--- a/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.c
+++ b/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.c
@@ -25,6 +25,7 @@
 #include <gst/gl/gl.h>
 #include <gst/gl/gstglfuncs.h>
 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+#include <gstgldmabufbufferpool.h>
 #include <gst/gl/egl/gsteglimage.h>
 #include <gst/allocators/gstdmabuf.h>
 #endif
@@ -899,6 +900,8 @@ gst_gl_download_element_stop (GstBaseTransform * bt)
     dl->dmabuf_allocator = NULL;
   }
 
+  gst_clear_object (&dl->foreign_dmabuf_pool);
+
   return TRUE;
 }
 
@@ -939,16 +942,82 @@ gst_gl_download_element_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
   return TRUE;
 }
 
-static GstCaps *
-_set_caps_features (const GstCaps * caps, const gchar * feature_name)
+#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+static gboolean
+_convert_dma_drm (GstGLContext * context, GstStructure * s)
 {
-  GstCaps *tmp = gst_caps_copy (caps);
-  guint n = gst_caps_get_size (tmp);
-  guint i = 0;
+  const GValue *fmtval = gst_structure_get_value (s, "format");
 
-  for (i = 0; i < n; i++)
-    gst_caps_set_features (tmp, i,
+  if (!fmtval) {
+    return FALSE;
+  }
+
+  if (G_VALUE_HOLDS_STRING (fmtval) &&
+      g_str_equal (g_value_get_string (fmtval), "DMA_DRM")) {
+    const GValue *drmval = gst_structure_get_value (s, "drm-format");
+    GValue newfmtval = G_VALUE_INIT;
+
+    if (context && gst_gl_dma_buf_transform_drm_formats_to_gst_formats (context,
+        drmval, 0, &newfmtval)) {
+      gst_structure_set_value (s, "format", &newfmtval);
+      gst_structure_remove_field (s, "drm-format");
+      g_value_unset (&newfmtval);
+    } else {
+      return FALSE;
+    }
+  } else {
+    GValue drmfmtval = G_VALUE_INIT;
+
+    if (!context) {
+      gst_structure_remove_field (s, "drm-format");
+    } else if (gst_gl_dma_buf_transform_gst_formats_to_drm_formats (context,
+            fmtval, 0, &drmfmtval)) {
+      gst_structure_set_value (s, "drm-format", &drmfmtval);
+      g_value_unset (&drmfmtval);
+    } else {
+      return FALSE;
+    }
+
+    gst_structure_set (s, "format", G_TYPE_STRING,
+        gst_video_format_to_string (GST_VIDEO_FORMAT_DMA_DRM), NULL);
+  }
+
+  return TRUE;
+}
+#endif
+
+static GstCaps *
+_set_caps_features (GstGLContext * context, const GstCaps * caps,
+    const gchar * feature_name)
+{
+  GstCaps *tmp = gst_caps_new_empty ();
+  guint n = gst_caps_get_size (caps);
+  guint i = 0;
+#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+  const gboolean new_feature_is_dmabuf =
+      g_str_equal (feature_name, GST_CAPS_FEATURE_MEMORY_DMABUF);
+#endif
+
+
+  for (i = 0; i < n; i++) {
+    GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
+
+#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+    gboolean old_feature_is_dmabuf =
+        gst_caps_features_contains (gst_caps_get_features (caps, i),
+        GST_CAPS_FEATURE_MEMORY_DMABUF);
+
+    if (new_feature_is_dmabuf != old_feature_is_dmabuf) {
+      if (!_convert_dma_drm (context, s)) {
+        gst_clear_structure (&s);
+        continue;
+      }
+    }
+#endif
+
+    gst_caps_append_structure_full (tmp, s,
         gst_caps_features_new_single_static_str (feature_name));
+  }
 
   return tmp;
 }
@@ -969,32 +1038,43 @@ static GstCaps *
 gst_gl_download_element_transform_caps (GstBaseTransform * bt,
     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
 {
+  GstGLBaseFilter *base_filter = GST_GL_BASE_FILTER (bt);
+
+  GstGLContext *context;
   GstCaps *result, *tmp;
 
+  if (base_filter->display && !gst_gl_base_filter_find_gl_context (base_filter))
+    return NULL;
+
+  context = gst_gl_base_filter_get_gl_context (base_filter);
+
   if (direction == GST_PAD_SRC) {
-    GstCaps *sys_caps = gst_caps_simplify (_set_caps_features (caps,
+    GstCaps *sys_caps = gst_caps_simplify (_set_caps_features (context, caps,
             GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY));
 
-    tmp = _set_caps_features (sys_caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
+    tmp = _set_caps_features (context, sys_caps,
+        GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
     tmp = gst_caps_merge (tmp, sys_caps);
   } else {
     GstCaps *newcaps;
     tmp = gst_caps_ref (caps);
 
 #if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
-    newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_NVMM);
+    newcaps = _set_caps_features (context, caps, GST_CAPS_FEATURE_MEMORY_NVMM);
     _remove_field (newcaps, "texture-target");
     // FIXME: RGBA-only?
     tmp = gst_caps_merge (tmp, newcaps);
 #endif
 
 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
-    newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
+    newcaps =
+        _set_caps_features (context, caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
     _remove_field (newcaps, "texture-target");
     tmp = gst_caps_merge (tmp, newcaps);
 #endif
 
-    newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
+    newcaps = _set_caps_features (context, caps,
+        GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
     _remove_field (newcaps, "texture-target");
     tmp = gst_caps_merge (tmp, newcaps);
   }
@@ -1292,6 +1372,16 @@ gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
   }
 #endif
 #if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+  if (gst_is_gl_dmabuf_buffer (inbuf)) {
+    GstBuffer *wrapped_dmabuf = gst_gl_dmabuf_buffer_unwrap (inbuf);
+
+    if (wrapped_dmabuf) {
+      *outbuf = wrapped_dmabuf;
+
+      return GST_FLOW_OK;
+    }
+  }
+
   if (dl->mode == GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS) {
     GstBuffer *buffer = _try_export_dmabuf (dl, inbuf);
 
@@ -1306,14 +1396,15 @@ gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
 
       *outbuf = buffer;
     } else {
-      GstCaps *src_caps;
-      GstCapsFeatures *features;
+      GstCaps *sink_caps, *src_caps;
       gboolean ret;
 
-      src_caps = gst_pad_get_current_caps (bt->srcpad);
-      src_caps = gst_caps_make_writable (src_caps);
-      features = gst_caps_get_features (src_caps, 0);
-      gst_caps_features_remove (features, GST_CAPS_FEATURE_MEMORY_DMABUF);
+      sink_caps = gst_pad_get_current_caps (bt->sinkpad);
+      src_caps = _set_caps_features (context, sink_caps,
+          GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
+      _remove_field (src_caps, "texture-target");
+      gst_caps_unref (sink_caps);
+
       g_atomic_int_set (&dl->try_dmabuf_exports, FALSE);
       dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
 
@@ -1374,6 +1465,27 @@ gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
     download->add_videometa = FALSE;
   }
 
+#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+  {
+    GstCaps *caps;
+    const GstCapsFeatures *features;
+
+    gst_clear_object (&download->foreign_dmabuf_pool);
+
+    gst_query_parse_allocation (query, &caps, NULL);
+    features = gst_caps_get_features (caps, 0);
+
+    if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF) &&
+        gst_query_get_n_allocation_pools (query) > 0) {
+
+      gst_query_parse_nth_allocation_pool (query, 0,
+          &download->foreign_dmabuf_pool, NULL, NULL, NULL);
+
+      gst_query_remove_nth_allocation_pool (query, 0);
+    }
+  }
+#endif
+
   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
       query);
 }
@@ -1446,6 +1558,16 @@ gst_gl_download_element_propose_allocation (GstBaseTransform * bt,
     }
   }
 #endif
+
+#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
+  if (!pool && GST_GL_DOWNLOAD_ELEMENT (bt)->foreign_dmabuf_pool) {
+    pool = gst_gl_dmabuf_buffer_pool_new (context,
+        GST_GL_DOWNLOAD_ELEMENT (bt)->foreign_dmabuf_pool);
+
+    GST_LOG_OBJECT (bt, "offering dma-buf-backed GL buffer pool");
+  }
+#endif
+
   if (!pool) {
     pool = gst_gl_buffer_pool_new (context);
   }
diff --git a/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.h b/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.h
index 7ae7c44ecd..64017c94d4 100644
--- a/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.h
+++ b/subprojects/gst-plugins-base/ext/gl/gstgldownloadelement.h
@@ -54,6 +54,7 @@ struct _GstGLDownloadElement
   GstGlDownloadMode mode;
   gboolean try_dmabuf_exports;
   GstAllocator * dmabuf_allocator;
+  GstBufferPool * foreign_dmabuf_pool;
   gboolean add_videometa;
 };
 
diff --git a/subprojects/gst-plugins-base/ext/gl/meson.build b/subprojects/gst-plugins-base/ext/gl/meson.build
index 743760d837..e38ad88db4 100644
--- a/subprojects/gst-plugins-base/ext/gl/meson.build
+++ b/subprojects/gst-plugins-base/ext/gl/meson.build
@@ -93,6 +93,12 @@ if png_dep.found()
   endif
 endif
 
+if glconf.get('GST_GL_HAVE_DMABUF', 0) == 1
+  opengl_sources += [
+    'gstgldmabufbufferpool.c',
+  ]
+endif
+
 if glconf.get('GST_GL_HAVE_WINDOW_COCOA', 0) == 1
   corefoundation_dep = dependency('appleframeworks', modules : ['CoreFoundation'], required : false)
   foundation_dep = dependency('appleframeworks', modules : ['Foundation'], required : false)