diff --git a/sys/d3d11/gstd3d11download.c b/sys/d3d11/gstd3d11download.c index 456ebf7acb..769e88b9e0 100644 --- a/sys/d3d11/gstd3d11download.c +++ b/sys/d3d11/gstd3d11download.c @@ -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; +} diff --git a/sys/d3d11/gstd3d11upload.c b/sys/d3d11/gstd3d11upload.c index 77d539908f..42d0caaae0 100644 --- a/sys/d3d11/gstd3d11upload.c +++ b/sys/d3d11/gstd3d11upload.c @@ -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; +} diff --git a/sys/d3d11/gstd3d11utils.c b/sys/d3d11/gstd3d11utils.c index e97bc7e6c7..55f63d16de 100644 --- a/sys/d3d11/gstd3d11utils.c +++ b/sys/d3d11/gstd3d11utils.c @@ -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 = ¶ms->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; +} diff --git a/sys/d3d11/gstd3d11utils.h b/sys/d3d11/gstd3d11utils.h index 109b208d9f..b87b0a45bb 100644 --- a/sys/d3d11/gstd3d11utils.h +++ b/sys/d3d11/gstd3d11utils.h @@ -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,