• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2019> Seungha Yang <seungha.yang@navercorp.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-d3d11download
22  * @title: d3d11download
23  * @short_description: Downloads Direct3D11 texture memory into system memory
24  *
25  * Downloads Direct3D11 texture memory into system memory
26  *
27  * ## Example launch line
28  * ```
29  * gst-launch-1.0 filesrc location=test_h264.mp4 ! parsebin ! d3d11h264dec ! \
30  *   d3d11convert ! d3d11download ! video/x-raw,width=640,height=480 ! mfh264enc ! \
31  *   h264parse ! mp4mux ! filesink location=output.mp4
32  * ```
33  * This pipeline will resize decoded (by #d3d11h264dec) frames to 640x480
34  * resolution by using #d3d11convert. Then it will be copied into system memory
35  * by d3d11download. Finally downloaded frames will be encoded as a new
36  * H.264 stream via #mfh264enc and muxed via mp4mux
37  *
38  * Since: 1.18
39  *
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 #  include <config.h>
44 #endif
45 
46 #include "gstd3d11download.h"
47 
48 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_download_debug);
49 #define GST_CAT_DEFAULT gst_d3d11_download_debug
50 
51 static GstStaticCaps sink_template_caps =
52     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
53     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_ALL_FORMATS) "; "
54     GST_VIDEO_CAPS_MAKE_WITH_FEATURES
55     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
56         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
57         GST_D3D11_ALL_FORMATS) "; "
58     GST_VIDEO_CAPS_MAKE (GST_D3D11_ALL_FORMATS) "; "
59     GST_VIDEO_CAPS_MAKE_WITH_FEATURES
60     (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
61         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
62         GST_D3D11_ALL_FORMATS));
63 
64 static GstStaticCaps src_template_caps =
65     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
66     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_ALL_FORMATS) "; "
67     GST_VIDEO_CAPS_MAKE_WITH_FEATURES
68     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
69         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
70         GST_D3D11_ALL_FORMATS) "; "
71     GST_VIDEO_CAPS_MAKE (GST_D3D11_ALL_FORMATS) "; "
72     GST_VIDEO_CAPS_MAKE_WITH_FEATURES
73     (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
74         GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
75         GST_D3D11_ALL_FORMATS));
76 
77 struct _GstD3D11Download
78 {
79   GstD3D11BaseFilter parent;
80 
81   GstBuffer *staging_buffer;
82 };
83 
84 #define gst_d3d11_download_parent_class parent_class
85 G_DEFINE_TYPE (GstD3D11Download, gst_d3d11_download,
86     GST_TYPE_D3D11_BASE_FILTER);
87 
88 static void gst_d3d11_download_dispose (GObject * object);
89 static gboolean gst_d3d11_download_stop (GstBaseTransform * trans);
90 static gboolean gst_d3d11_download_sink_event (GstBaseTransform * trans,
91     GstEvent * event);
92 static GstCaps *gst_d3d11_download_transform_caps (GstBaseTransform * trans,
93     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
94 static gboolean gst_d3d11_download_propose_allocation (GstBaseTransform * trans,
95     GstQuery * decide_query, GstQuery * query);
96 static gboolean gst_d3d11_download_decide_allocation (GstBaseTransform * trans,
97     GstQuery * query);
98 static GstFlowReturn gst_d3d11_download_transform (GstBaseTransform * trans,
99     GstBuffer * inbuf, GstBuffer * outbuf);
100 static gboolean gst_d3d11_download_set_info (GstD3D11BaseFilter * filter,
101     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
102     GstVideoInfo * out_info);
103 
104 static void
gst_d3d11_download_class_init(GstD3D11DownloadClass * klass)105 gst_d3d11_download_class_init (GstD3D11DownloadClass * klass)
106 {
107   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
108   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
109   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
110   GstD3D11BaseFilterClass *bfilter_class = GST_D3D11_BASE_FILTER_CLASS (klass);
111   GstCaps *caps;
112 
113   gobject_class->dispose = gst_d3d11_download_dispose;
114 
115   caps = gst_d3d11_get_updated_template_caps (&sink_template_caps);
116   gst_element_class_add_pad_template (element_class,
117       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
118   gst_caps_unref (caps);
119 
120   caps = gst_d3d11_get_updated_template_caps (&src_template_caps);
121   gst_element_class_add_pad_template (element_class,
122       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps));
123   gst_caps_unref (caps);
124 
125   gst_element_class_set_static_metadata (element_class,
126       "Direct3D11 downloader", "Filter/Video",
127       "Downloads Direct3D11 texture memory into system memory",
128       "Seungha Yang <seungha.yang@navercorp.com>");
129 
130   trans_class->passthrough_on_same_caps = TRUE;
131 
132   trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_download_stop);
133   trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_d3d11_download_sink_event);
134   trans_class->transform_caps =
135       GST_DEBUG_FUNCPTR (gst_d3d11_download_transform_caps);
136   trans_class->propose_allocation =
137       GST_DEBUG_FUNCPTR (gst_d3d11_download_propose_allocation);
138   trans_class->decide_allocation =
139       GST_DEBUG_FUNCPTR (gst_d3d11_download_decide_allocation);
140   trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_download_transform);
141 
142   bfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_d3d11_download_set_info);
143 
144   GST_DEBUG_CATEGORY_INIT (gst_d3d11_download_debug,
145       "d3d11download", 0, "d3d11download Element");
146 }
147 
148 static void
gst_d3d11_download_init(GstD3D11Download * download)149 gst_d3d11_download_init (GstD3D11Download * download)
150 {
151 }
152 
153 static void
gst_d3d11_download_dispose(GObject * object)154 gst_d3d11_download_dispose (GObject * object)
155 {
156   GstD3D11Download *self = GST_D3D11_DOWNLOAD (object);
157 
158   gst_clear_buffer (&self->staging_buffer);
159 
160   G_OBJECT_CLASS (parent_class)->dispose (object);
161 }
162 
163 static gboolean
gst_d3d11_download_stop(GstBaseTransform * trans)164 gst_d3d11_download_stop (GstBaseTransform * trans)
165 {
166   GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
167 
168   gst_clear_buffer (&self->staging_buffer);
169 
170   return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (trans);
171 }
172 
173 static gboolean
gst_d3d11_download_sink_event(GstBaseTransform * trans,GstEvent * event)174 gst_d3d11_download_sink_event (GstBaseTransform * trans, GstEvent * event)
175 {
176   GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
177 
178   switch (GST_EVENT_TYPE (event)) {
179     case GST_EVENT_EOS:
180       /* We don't need to hold this staging buffer after eos */
181       gst_clear_buffer (&self->staging_buffer);
182       break;
183     default:
184       break;
185   }
186 
187   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
188 }
189 
190 static GstCaps *
_set_caps_features(const GstCaps * caps,const gchar * feature_name)191 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
192 {
193   GstCaps *tmp = gst_caps_copy (caps);
194   guint n = gst_caps_get_size (tmp);
195   guint i = 0;
196 
197   for (i = 0; i < n; i++)
198     gst_caps_set_features (tmp, i,
199         gst_caps_features_from_string (feature_name));
200 
201   return tmp;
202 }
203 
204 static GstCaps *
gst_d3d11_download_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)205 gst_d3d11_download_transform_caps (GstBaseTransform * trans,
206     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
207 {
208   GstCaps *result, *tmp;
209 
210   GST_DEBUG_OBJECT (trans,
211       "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
212       (direction == GST_PAD_SINK) ? "sink" : "src");
213 
214   if (direction == GST_PAD_SINK) {
215     tmp = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
216     tmp = gst_caps_merge (gst_caps_ref (caps), tmp);
217   } else {
218     GstCaps *newcaps;
219     tmp = gst_caps_ref (caps);
220 
221     newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY);
222     tmp = gst_caps_merge (tmp, newcaps);
223   }
224 
225   if (filter) {
226     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
227     gst_caps_unref (tmp);
228   } else {
229     result = tmp;
230   }
231 
232   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, result);
233 
234   return result;
235 }
236 
237 static gboolean
gst_d3d11_download_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)238 gst_d3d11_download_propose_allocation (GstBaseTransform * trans,
239     GstQuery * decide_query, GstQuery * query)
240 {
241   GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
242   GstVideoInfo info;
243   GstBufferPool *pool;
244   GstCaps *caps;
245   guint size;
246 
247   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
248           decide_query, query))
249     return FALSE;
250 
251   /* passthrough, we're done */
252   if (decide_query == NULL)
253     return TRUE;
254 
255   gst_query_parse_allocation (query, &caps, NULL);
256 
257   if (caps == NULL)
258     return FALSE;
259 
260   if (!gst_video_info_from_caps (&info, caps))
261     return FALSE;
262 
263   if (gst_query_get_n_allocation_pools (query) == 0) {
264     GstCapsFeatures *features;
265     GstStructure *config;
266     gboolean is_d3d11 = FALSE;
267 
268     features = gst_caps_get_features (caps, 0);
269 
270     if (features && gst_caps_features_contains (features,
271             GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
272       GST_DEBUG_OBJECT (filter, "upstream support d3d11 memory");
273       pool = gst_d3d11_buffer_pool_new (filter->device);
274       is_d3d11 = TRUE;
275     } else {
276       pool = gst_video_buffer_pool_new ();
277     }
278 
279     config = gst_buffer_pool_get_config (pool);
280 
281     gst_buffer_pool_config_add_option (config,
282         GST_BUFFER_POOL_OPTION_VIDEO_META);
283 
284     /* d3d11 pool does not support video alignment */
285     if (!is_d3d11) {
286       gst_buffer_pool_config_add_option (config,
287           GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
288     }
289 
290     size = GST_VIDEO_INFO_SIZE (&info);
291     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
292 
293     if (!gst_buffer_pool_set_config (pool, config))
294       goto config_failed;
295 
296     gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
297 
298     if (is_d3d11) {
299       /* d3d11 buffer pool will update buffer size based on allocated texture,
300        * get size from config again */
301       config = gst_buffer_pool_get_config (pool);
302       gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr,
303           nullptr);
304       gst_structure_free (config);
305     }
306 
307     gst_query_add_allocation_pool (query, pool, size, 0, 0);
308 
309     gst_object_unref (pool);
310   }
311 
312   return TRUE;
313 
314   /* ERRORS */
315 config_failed:
316   {
317     GST_ERROR_OBJECT (filter, "failed to set config");
318     gst_object_unref (pool);
319     return FALSE;
320   }
321 }
322 
323 static gboolean
gst_d3d11_download_decide_allocation(GstBaseTransform * trans,GstQuery * query)324 gst_d3d11_download_decide_allocation (GstBaseTransform * trans,
325     GstQuery * query)
326 {
327   GstBufferPool *pool = NULL;
328   GstStructure *config;
329   guint min, max, size;
330   gboolean update_pool;
331   GstCaps *outcaps = NULL;
332 
333   if (gst_query_get_n_allocation_pools (query) > 0) {
334     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
335 
336     if (!pool)
337       gst_query_parse_allocation (query, &outcaps, NULL);
338 
339     update_pool = TRUE;
340   } else {
341     GstVideoInfo vinfo;
342 
343     gst_query_parse_allocation (query, &outcaps, NULL);
344     gst_video_info_from_caps (&vinfo, outcaps);
345     size = vinfo.size;
346     min = max = 0;
347     update_pool = FALSE;
348   }
349 
350   if (!pool)
351     pool = gst_video_buffer_pool_new ();
352 
353   config = gst_buffer_pool_get_config (pool);
354   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
355   if (outcaps)
356     gst_buffer_pool_config_set_params (config, outcaps, size, 0, 0);
357   gst_buffer_pool_set_config (pool, config);
358 
359   if (update_pool)
360     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
361   else
362     gst_query_add_allocation_pool (query, pool, size, min, max);
363 
364   gst_object_unref (pool);
365 
366   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
367       query);
368 }
369 
370 static gboolean
gst_d3d11_download_can_use_staging_buffer(GstD3D11Download * self,GstBuffer * inbuf)371 gst_d3d11_download_can_use_staging_buffer (GstD3D11Download * self,
372     GstBuffer * inbuf)
373 {
374   GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (self);
375   ID3D11Device *device_handle =
376       gst_d3d11_device_get_device_handle (filter->device);
377 
378   if (!gst_d3d11_buffer_can_access_device (inbuf, device_handle))
379     return FALSE;
380 
381   if (self->staging_buffer)
382     return TRUE;
383 
384   self->staging_buffer = gst_d3d11_allocate_staging_buffer_for (inbuf,
385       &filter->in_info, TRUE);
386 
387   if (!self->staging_buffer) {
388     GST_WARNING_OBJECT (self, "Couldn't allocate staging buffer");
389     return FALSE;
390   }
391 
392   return TRUE;
393 }
394 
395 static GstFlowReturn
gst_d3d11_download_transform(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)396 gst_d3d11_download_transform (GstBaseTransform * trans, GstBuffer * inbuf,
397     GstBuffer * outbuf)
398 {
399   GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
400   GstD3D11Download *self = GST_D3D11_DOWNLOAD (trans);
401   GstVideoFrame in_frame, out_frame;
402   GstFlowReturn ret = GST_FLOW_OK;
403   gboolean use_staging_buf;
404   GstBuffer *target_inbuf = inbuf;
405   guint i;
406 
407   use_staging_buf = gst_d3d11_download_can_use_staging_buffer (self, inbuf);
408 
409   if (use_staging_buf) {
410     GST_TRACE_OBJECT (self, "Copy input buffer to staging buffer");
411 
412     /* Copy d3d11 texture to staging texture */
413     if (!gst_d3d11_buffer_copy_into (self->staging_buffer,
414             inbuf, &filter->in_info)) {
415       GST_ERROR_OBJECT (self,
416           "Failed to copy input buffer into staging texture");
417       return GST_FLOW_ERROR;
418     }
419 
420     target_inbuf = self->staging_buffer;
421   }
422 
423   if (!gst_video_frame_map (&in_frame, &filter->in_info, target_inbuf,
424           (GstMapFlags) (GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF)))
425     goto invalid_buffer;
426 
427   if (!gst_video_frame_map (&out_frame, &filter->out_info, outbuf,
428           (GstMapFlags) (GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
429     gst_video_frame_unmap (&in_frame);
430     goto invalid_buffer;
431   }
432 
433   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&in_frame); i++) {
434     if (!gst_video_frame_copy_plane (&out_frame, &in_frame, i)) {
435       GST_ERROR_OBJECT (filter, "Couldn't copy %dth plane", i);
436       ret = GST_FLOW_ERROR;
437       break;
438     }
439   }
440 
441   gst_video_frame_unmap (&out_frame);
442   gst_video_frame_unmap (&in_frame);
443 
444   return ret;
445 
446   /* ERRORS */
447 invalid_buffer:
448   {
449     GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
450         ("invalid video buffer received"));
451     return GST_FLOW_ERROR;
452   }
453 }
454 
455 static gboolean
gst_d3d11_download_set_info(GstD3D11BaseFilter * filter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)456 gst_d3d11_download_set_info (GstD3D11BaseFilter * filter,
457     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
458     GstVideoInfo * out_info)
459 {
460   GstD3D11Download *self = GST_D3D11_DOWNLOAD (filter);
461 
462   gst_clear_buffer (&self->staging_buffer);
463 
464   return TRUE;
465 }
466