d3d11upload/d3d11download: Make use of staging buffer
authorSeungha Yang <seungha@centricular.com>
Wed, 30 Sep 2020 18:47:13 +0000 (03:47 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 4 Oct 2020 15:44:03 +0000 (15:44 +0000)
... instead of direct cpu map for d3d11memory object. In this way,
we don't need per GstD3D11Memory staging texture.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1627>

sys/d3d11/gstd3d11download.c
sys/d3d11/gstd3d11upload.c
sys/d3d11/gstd3d11utils.c
sys/d3d11/gstd3d11utils.h

index 456ebf7..769e88b 100644 (file)
@@ -65,12 +65,18 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
 struct _GstD3D11Download
 {
   GstD3D11BaseFilter parent;
+
+  GstBuffer *staging_buffer;
 };
 
 #define gst_d3d11_download_parent_class parent_class
 G_DEFINE_TYPE (GstD3D11Download, gst_d3d11_download,
     GST_TYPE_D3D11_BASE_FILTER);
 
+static void gst_d3d11_download_dispose (GObject * object);
+static gboolean gst_d3d11_download_stop (GstBaseTransform * trans);
+static gboolean gst_d3d11_download_sink_event (GstBaseTransform * trans,
+    GstEvent * event);
 static GstCaps *gst_d3d11_download_transform_caps (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
 static gboolean gst_d3d11_download_propose_allocation (GstBaseTransform * trans,
@@ -79,12 +85,19 @@ static gboolean gst_d3d11_download_decide_allocation (GstBaseTransform * trans,
     GstQuery * query);
 static GstFlowReturn gst_d3d11_download_transform (GstBaseTransform * trans,
     GstBuffer * inbuf, GstBuffer * outbuf);
+static gboolean gst_d3d11_download_set_info (GstD3D11BaseFilter * filter,
+    GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
+    GstVideoInfo * out_info);
 
 static void
 gst_d3d11_download_class_init (GstD3D11DownloadClass * klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
+  GstD3D11BaseFilterClass *bfilter_class = GST_D3D11_BASE_FILTER_CLASS (klass);
+
+  gobject_class->dispose = gst_d3d11_download_dispose;
 
   gst_element_class_add_static_pad_template (element_class, &sink_template);
   gst_element_class_add_static_pad_template (element_class, &src_template);
@@ -96,6 +109,8 @@ gst_d3d11_download_class_init (GstD3D11DownloadClass * klass)
 
   trans_class->passthrough_on_same_caps = TRUE;
 
+  trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_download_stop);
+  trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_d3d11_download_sink_event);
   trans_class->transform_caps =
       GST_DEBUG_FUNCPTR (gst_d3d11_download_transform_caps);
   trans_class->propose_allocation =
@@ -104,6 +119,8 @@ gst_d3d11_download_class_init (GstD3D11DownloadClass * klass)
       GST_DEBUG_FUNCPTR (gst_d3d11_download_decide_allocation);
   trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_download_transform);
 
+  bfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_d3d11_download_set_info);
+
   GST_DEBUG_CATEGORY_INIT (gst_d3d11_download_debug,
       "d3d11download", 0, "d3d11download Element");
 }
@@ -113,6 +130,43 @@ gst_d3d11_download_init (GstD3D11Download * download)
 {
 }
 
+static void
+gst_d3d11_download_dispose (GObject * object)
+{
+  GstD3D11Download *self = GST_D3D11_DOWNLOAD (object);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static gboolean
+gst_d3d11_download_stop (GstBaseTransform * trans)
+{
+  GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (trans);
+}
+
+static gboolean
+gst_d3d11_download_sink_event (GstBaseTransform * trans, GstEvent * event)
+{
+  GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* We don't need to hold this staging buffer after eos */
+      gst_clear_buffer (&self->staging_buffer);
+      break;
+    default:
+      break;
+  }
+
+  return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
+}
+
 static GstCaps *
 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
 {
@@ -289,15 +343,63 @@ gst_d3d11_download_decide_allocation (GstBaseTransform * trans,
       query);
 }
 
+static gboolean
+gst_d3d11_download_can_use_staging_buffer (GstD3D11Download * self,
+    GstBuffer * inbuf)
+{
+  GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (self);
+  gint i;
+
+  /* staging buffer doesn't need to be used for non-d3d11 memory */
+  for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
+    GstMemory *mem = gst_buffer_peek_memory (inbuf, i);
+
+    if (!gst_is_d3d11_memory (mem))
+      return FALSE;
+  }
+
+  if (self->staging_buffer)
+    return TRUE;
+
+  self->staging_buffer = gst_d3d11_allocate_staging_buffer_for (inbuf,
+      &filter->in_info, TRUE);
+
+  if (!self->staging_buffer) {
+    GST_WARNING_OBJECT (self, "Couldn't allocate staging buffer");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 static GstFlowReturn
 gst_d3d11_download_transform (GstBaseTransform * trans, GstBuffer * inbuf,
     GstBuffer * outbuf)
 {
   GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
+  GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
   GstVideoFrame in_frame, out_frame;
+  GstFlowReturn ret = GST_FLOW_OK;
+  gboolean use_staging_buf;
+  GstBuffer *target_inbuf = inbuf;
   gint i;
 
-  if (!gst_video_frame_map (&in_frame, &filter->in_info, inbuf,
+  use_staging_buf = gst_d3d11_download_can_use_staging_buffer (self, inbuf);
+
+  if (use_staging_buf) {
+    GST_TRACE_OBJECT (self, "Copy input buffer to staging buffer");
+
+    /* Copy d3d11 texture to staging texture */
+    if (!gst_d3d11_buffer_copy_into (self->staging_buffer, inbuf)) {
+      GST_ERROR_OBJECT (self,
+          "Failed to copy input buffer into staging texture");
+      return GST_FLOW_ERROR;
+    }
+
+    target_inbuf = self->staging_buffer;
+  }
+
+  if (!gst_video_frame_map (&in_frame, &filter->in_info, target_inbuf,
           GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))
     goto invalid_buffer;
 
@@ -310,14 +412,15 @@ gst_d3d11_download_transform (GstBaseTransform * trans, GstBuffer * inbuf,
   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&in_frame); i++) {
     if (!gst_video_frame_copy_plane (&out_frame, &in_frame, i)) {
       GST_ERROR_OBJECT (filter, "Couldn't copy %dth plane", i);
-      return GST_FLOW_ERROR;
+      ret = GST_FLOW_ERROR;
+      break;
     }
   }
 
   gst_video_frame_unmap (&out_frame);
   gst_video_frame_unmap (&in_frame);
 
-  return GST_FLOW_OK;
+  return ret;
 
   /* ERRORS */
 invalid_buffer:
@@ -327,3 +430,15 @@ invalid_buffer:
     return GST_FLOW_ERROR;
   }
 }
+
+static gboolean
+gst_d3d11_download_set_info (GstD3D11BaseFilter * filter,
+    GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
+    GstVideoInfo * out_info)
+{
+  GstD3D11Download *self = GST_D3D11_DOWNLOAD (filter);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  return TRUE;
+}
index 77d5399..42d0caa 100644 (file)
@@ -60,11 +60,17 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
 struct _GstD3D11Upload
 {
   GstD3D11BaseFilter parent;
+
+  GstBuffer *staging_buffer;
 };
 
 #define gst_d3d11_upload_parent_class parent_class
 G_DEFINE_TYPE (GstD3D11Upload, gst_d3d11_upload, GST_TYPE_D3D11_BASE_FILTER);
 
+static void gst_d3d11_upload_dispose (GObject * object);
+static gboolean gst_d3d11_upload_stop (GstBaseTransform * trans);
+static gboolean gst_d3d11_upload_sink_event (GstBaseTransform * trans,
+    GstEvent * event);
 static GstCaps *gst_d3d11_upload_transform_caps (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
 static gboolean gst_d3d11_upload_propose_allocation (GstBaseTransform * trans,
@@ -73,12 +79,19 @@ static gboolean gst_d3d11_upload_decide_allocation (GstBaseTransform * trans,
     GstQuery * query);
 static GstFlowReturn gst_d3d11_upload_transform (GstBaseTransform * trans,
     GstBuffer * inbuf, GstBuffer * outbuf);
+static gboolean gst_d3d11_upload_set_info (GstD3D11BaseFilter * filter,
+    GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
+    GstVideoInfo * out_info);
 
 static void
 gst_d3d11_upload_class_init (GstD3D11UploadClass * klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
+  GstD3D11BaseFilterClass *bfilter_class = GST_D3D11_BASE_FILTER_CLASS (klass);
+
+  gobject_class->dispose = gst_d3d11_upload_dispose;
 
   gst_element_class_add_static_pad_template (element_class, &sink_template);
   gst_element_class_add_static_pad_template (element_class, &src_template);
@@ -90,6 +103,8 @@ gst_d3d11_upload_class_init (GstD3D11UploadClass * klass)
 
   trans_class->passthrough_on_same_caps = TRUE;
 
+  trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_upload_stop);
+  trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_d3d11_upload_sink_event);
   trans_class->transform_caps =
       GST_DEBUG_FUNCPTR (gst_d3d11_upload_transform_caps);
   trans_class->propose_allocation =
@@ -98,6 +113,8 @@ gst_d3d11_upload_class_init (GstD3D11UploadClass * klass)
       GST_DEBUG_FUNCPTR (gst_d3d11_upload_decide_allocation);
   trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_upload_transform);
 
+  bfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_d3d11_upload_set_info);
+
   GST_DEBUG_CATEGORY_INIT (gst_d3d11_upload_debug,
       "d3d11upload", 0, "d3d11upload Element");
 }
@@ -107,6 +124,43 @@ gst_d3d11_upload_init (GstD3D11Upload * upload)
 {
 }
 
+static void
+gst_d3d11_upload_dispose (GObject * object)
+{
+  GstD3D11Upload *self = GST_D3D11_UPLOAD (object);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static gboolean
+gst_d3d11_upload_stop (GstBaseTransform * trans)
+{
+  GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (trans);
+}
+
+static gboolean
+gst_d3d11_upload_sink_event (GstBaseTransform * trans, GstEvent * event)
+{
+  GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* We don't need to hold this staging buffer after eos */
+      gst_clear_buffer (&self->staging_buffer);
+      break;
+    default:
+      break;
+  }
+
+  return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
+}
+
 static GstCaps *
 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
 {
@@ -331,21 +385,59 @@ gst_d3d11_upload_decide_allocation (GstBaseTransform * trans, GstQuery * query)
       query);
 }
 
+static gboolean
+gst_d3d11_upload_can_use_staging_buffer (GstD3D11Upload * self,
+    GstBuffer * outbuf)
+{
+  GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (self);
+  gint i;
+
+  /* staging buffer doesn't need to be used for non-d3d11 memory */
+  for (i = 0; i < gst_buffer_n_memory (outbuf); i++) {
+    GstMemory *mem = gst_buffer_peek_memory (outbuf, i);
+
+    if (!gst_is_d3d11_memory (mem))
+      return FALSE;
+  }
+
+  if (self->staging_buffer)
+    return TRUE;
+
+  self->staging_buffer = gst_d3d11_allocate_staging_buffer_for (outbuf,
+      &filter->out_info, TRUE);
+
+  if (!self->staging_buffer) {
+    GST_WARNING_OBJECT (self, "Couldn't allocate staging buffer");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 static GstFlowReturn
 gst_d3d11_upload_transform (GstBaseTransform * trans, GstBuffer * inbuf,
     GstBuffer * outbuf)
 {
   GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
-
+  GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
   GstVideoFrame in_frame, out_frame;
-  gint i;
   GstFlowReturn ret = GST_FLOW_OK;
+  gboolean use_staging_buf;
+  GstBuffer *target_outbuf = outbuf;
+  gint i;
+
+  use_staging_buf = gst_d3d11_upload_can_use_staging_buffer (self, outbuf);
+
+  if (use_staging_buf) {
+    GST_TRACE_OBJECT (self, "Copy input buffer to staging buffer");
+    target_outbuf = self->staging_buffer;
+  }
 
   if (!gst_video_frame_map (&in_frame, &filter->in_info, inbuf,
           GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))
     goto invalid_buffer;
 
-  if (!gst_video_frame_map (&out_frame, &filter->out_info, outbuf,
+  if (!gst_video_frame_map (&out_frame, &filter->out_info, target_outbuf,
           GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF)) {
     gst_video_frame_unmap (&in_frame);
     goto invalid_buffer;
@@ -353,7 +445,7 @@ gst_d3d11_upload_transform (GstBaseTransform * trans, GstBuffer * inbuf,
 
   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&in_frame); i++) {
     if (!gst_video_frame_copy_plane (&out_frame, &in_frame, i)) {
-      GST_ERROR_OBJECT (filter, "Couldn't copy %dth plane", i);
+      GST_ERROR_OBJECT (filter, "Couldn't copy plane %d", i);
       ret = GST_FLOW_ERROR;
       break;
     }
@@ -362,6 +454,14 @@ gst_d3d11_upload_transform (GstBaseTransform * trans, GstBuffer * inbuf,
   gst_video_frame_unmap (&out_frame);
   gst_video_frame_unmap (&in_frame);
 
+  /* Copy staging texture to d3d11 texture */
+  if (use_staging_buf) {
+    if (!gst_d3d11_buffer_copy_into (outbuf, self->staging_buffer)) {
+      GST_ERROR_OBJECT (self, "Cannot copy staging texture into texture");
+      return GST_FLOW_ERROR;
+    }
+  }
+
   return ret;
 
   /* ERRORS */
@@ -372,3 +472,15 @@ invalid_buffer:
     return GST_FLOW_ERROR;
   }
 }
+
+static gboolean
+gst_d3d11_upload_set_info (GstD3D11BaseFilter * filter,
+    GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
+    GstVideoInfo * out_info)
+{
+  GstD3D11Upload *self = GST_D3D11_UPLOAD (filter);
+
+  gst_clear_buffer (&self->staging_buffer);
+
+  return TRUE;
+}
index e97bc7e..55f63d1 100644 (file)
@@ -471,6 +471,80 @@ error:
   return NULL;
 }
 
+GstBuffer *
+gst_d3d11_allocate_staging_buffer_for (GstBuffer * buffer,
+    const GstVideoInfo * info, gboolean add_videometa)
+{
+  GstD3D11Memory *dmem;
+  GstD3D11Device *device;
+  GstD3D11AllocationParams *params = NULL;
+  GstD3D11Allocator *alloc = NULL;
+  GstBuffer *staging_buffer = NULL;
+  D3D11_TEXTURE2D_DESC *desc;
+  gint i;
+
+  for (i = 0; i < gst_buffer_n_memory (buffer); i++) {
+    GstMemory *mem = gst_buffer_peek_memory (buffer, i);
+
+    if (!gst_is_d3d11_memory (mem)) {
+      GST_DEBUG ("Not a d3d11 memory");
+
+      return NULL;
+    }
+  }
+
+  dmem = (GstD3D11Memory *) gst_buffer_peek_memory (buffer, 0);
+  device = dmem->device;
+
+  params = gst_d3d11_allocation_params_new (device, (GstVideoInfo *) info,
+      0, 0);
+
+  if (!params) {
+    GST_WARNING ("Couldn't create alloc params");
+    goto done;
+  }
+
+  desc = &params->desc[0];
+  /* resolution of semi-planar formats must be multiple of 2 */
+  if (desc[0].Format == DXGI_FORMAT_NV12 || desc[0].Format == DXGI_FORMAT_P010
+      || desc[0].Format == DXGI_FORMAT_P016) {
+    if (desc[0].Width % 2 || desc[0].Height % 2) {
+      gint width, height;
+      GstVideoAlignment align;
+
+      width = GST_ROUND_UP_2 (desc[0].Width);
+      height = GST_ROUND_UP_2 (desc[0].Height);
+
+      gst_video_alignment_reset (&align);
+      align.padding_right = width - desc[0].Width;
+      align.padding_bottom = height - desc[0].Height;
+
+      gst_d3d11_allocation_params_alignment (params, &align);
+    }
+  }
+
+  alloc = gst_d3d11_allocator_new (device);
+  if (!alloc) {
+    GST_WARNING ("Couldn't create allocator");
+    goto done;
+  }
+
+  staging_buffer = gst_d3d11_allocate_staging_buffer (alloc,
+      info, params->d3d11_format, params->desc, add_videometa);
+
+  if (!staging_buffer)
+    GST_WARNING ("Couldn't allocate staging buffer");
+
+done:
+  if (params)
+    gst_d3d11_allocation_params_free (params);
+
+  if (alloc)
+    gst_object_unref (alloc);
+
+  return staging_buffer;
+}
+
 gboolean
 _gst_d3d11_result (HRESULT hr, GstD3D11Device * device, GstDebugCategory * cat,
     const gchar * file, const gchar * function, gint line)
@@ -504,3 +578,93 @@ _gst_d3d11_result (HRESULT hr, GstD3D11Device * device, GstDebugCategory * cat,
   return SUCCEEDED (hr);
 #endif
 }
+
+gboolean
+gst_d3d11_buffer_copy_into (GstBuffer * dst, GstBuffer * src)
+{
+  guint i;
+  guint num_mem;
+
+  g_return_val_if_fail (GST_IS_BUFFER (dst), FALSE);
+  g_return_val_if_fail (GST_IS_BUFFER (src), FALSE);
+
+  num_mem = gst_buffer_n_memory (dst);
+  if (num_mem != gst_buffer_n_memory (src)) {
+    GST_WARNING ("different num memory");
+    return FALSE;
+  }
+
+  for (i = 0; i < num_mem; i++) {
+    GstMemory *dst_mem, *src_mem;
+    GstD3D11Memory *dst_dmem, *src_dmem;
+    GstMapInfo dst_info;
+    GstMapInfo src_info;
+    ID3D11Resource *dst_texture, *src_texture;
+    ID3D11DeviceContext *device_context;
+    GstD3D11Device *device;
+    D3D11_BOX src_box = { 0, };
+
+    dst_mem = gst_buffer_peek_memory (dst, i);
+    src_mem = gst_buffer_peek_memory (src, i);
+
+    if (!gst_is_d3d11_memory (dst_mem)) {
+      GST_WARNING ("dst memory is not d3d11");
+      return FALSE;
+    }
+
+    if (!gst_is_d3d11_memory (src_mem)) {
+      GST_WARNING ("src memory is not d3d11");
+      return FALSE;
+    }
+
+    dst_dmem = (GstD3D11Memory *) dst_mem;
+    src_dmem = (GstD3D11Memory *) src_mem;
+
+    device = dst_dmem->device;
+    if (device != src_dmem->device) {
+      GST_WARNING ("different device");
+      return FALSE;
+    }
+
+    if (dst_dmem->desc.Format != src_dmem->desc.Format) {
+      GST_WARNING ("different dxgi format");
+      return FALSE;
+    }
+
+    device_context = gst_d3d11_device_get_device_context_handle (device);
+
+    if (!gst_memory_map (dst_mem, &dst_info, GST_MAP_WRITE | GST_MAP_D3D11)) {
+      GST_ERROR ("Cannot map dst d3d11 memory");
+      return FALSE;
+    }
+
+    if (!gst_memory_map (src_mem, &src_info, GST_MAP_READ | GST_MAP_D3D11)) {
+      GST_ERROR ("Cannot map src d3d11 memory");
+      gst_memory_unmap (dst_mem, &dst_info);
+      return FALSE;
+    }
+
+    dst_texture = (ID3D11Resource *) dst_info.data;
+    src_texture = (ID3D11Resource *) src_info.data;
+
+    /* src/dst texture size might be different if padding was used.
+     * select smaller size */
+    src_box.left = 0;
+    src_box.top = 0;
+    src_box.front = 0;
+    src_box.back = 1;
+    src_box.right = MIN (src_dmem->desc.Width, dst_dmem->desc.Width);
+    src_box.bottom = MIN (src_dmem->desc.Height, dst_dmem->desc.Height);
+
+    gst_d3d11_device_lock (device);
+    ID3D11DeviceContext_CopySubresourceRegion (device_context,
+        dst_texture, dst_dmem->subresource_index, 0, 0, 0,
+        src_texture, src_dmem->subresource_index, &src_box);
+    gst_d3d11_device_unlock (device);
+
+    gst_memory_unmap (src_mem, &src_info);
+    gst_memory_unmap (dst_mem, &dst_info);
+  }
+
+  return TRUE;
+}
index 109b208..b87b0a4 100644 (file)
@@ -60,6 +60,13 @@ GstBuffer *     gst_d3d11_allocate_staging_buffer   (GstD3D11Allocator * allocat
                                                      const D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES],
                                                      gboolean add_videometa);
 
+GstBuffer *     gst_d3d11_allocate_staging_buffer_for (GstBuffer * buffer,
+                                                       const GstVideoInfo * info,
+                                                       gboolean add_videometa);
+
+gboolean        gst_d3d11_buffer_copy_into          (GstBuffer * dst,
+                                                     GstBuffer * src);
+
 gboolean       _gst_d3d11_result                    (HRESULT hr,
                                                      GstD3D11Device * device,
                                                      GstDebugCategory * cat,