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-d3d11upload
22 * @title: d3d11upload
23 *
24 * Upload video frame to Direct3D11 texture memory
25 *
26 * ## Example launch line
27 * ```
28 * gst-launch-1.0 videotestsrc ! d3d11upload ! d3d11videosinkelement
29 * ```
30 * This pipeline will upload video test frame (system memory) into Direct3D11
31 * textures and d3d11videosinkelement will display frames on screen.
32 *
33 * Since: 1.18
34 *
35 */
36
37 #ifdef HAVE_CONFIG_H
38 # include <config.h>
39 #endif
40
41 #include "gstd3d11upload.h"
42
43 #include <string.h>
44
45 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_upload_debug);
46 #define GST_CAT_DEFAULT gst_d3d11_upload_debug
47
48 static GstStaticCaps sink_template_caps =
49 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_D3D11_ALL_FORMATS) "; "
50 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
51 (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
52 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
53 GST_D3D11_ALL_FORMATS) "; "
54 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
55 GST_D3D11_ALL_FORMATS) ";"
56 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY
57 "," GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
58 GST_D3D11_ALL_FORMATS));
59
60 static GstStaticCaps src_template_caps =
61 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
62 (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_ALL_FORMATS) "; "
63 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
64 (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
65 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
66 GST_D3D11_ALL_FORMATS) ";"
67 GST_VIDEO_CAPS_MAKE (GST_D3D11_ALL_FORMATS) "; "
68 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
69 (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
70 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
71 GST_D3D11_ALL_FORMATS));
72
73 struct _GstD3D11Upload
74 {
75 GstD3D11BaseFilter parent;
76
77 GstBuffer *staging_buffer;
78 };
79
80 #define gst_d3d11_upload_parent_class parent_class
81 G_DEFINE_TYPE (GstD3D11Upload, gst_d3d11_upload, GST_TYPE_D3D11_BASE_FILTER);
82
83 static void gst_d3d11_upload_dispose (GObject * object);
84 static gboolean gst_d3d11_upload_stop (GstBaseTransform * trans);
85 static gboolean gst_d3d11_upload_sink_event (GstBaseTransform * trans,
86 GstEvent * event);
87 static GstCaps *gst_d3d11_upload_transform_caps (GstBaseTransform * trans,
88 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
89 static gboolean gst_d3d11_upload_propose_allocation (GstBaseTransform * trans,
90 GstQuery * decide_query, GstQuery * query);
91 static gboolean gst_d3d11_upload_decide_allocation (GstBaseTransform * trans,
92 GstQuery * query);
93 static GstFlowReturn gst_d3d11_upload_transform (GstBaseTransform * trans,
94 GstBuffer * inbuf, GstBuffer * outbuf);
95 static gboolean gst_d3d11_upload_set_info (GstD3D11BaseFilter * filter,
96 GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
97 GstVideoInfo * out_info);
98
99 static void
gst_d3d11_upload_class_init(GstD3D11UploadClass * klass)100 gst_d3d11_upload_class_init (GstD3D11UploadClass * klass)
101 {
102 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
104 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
105 GstD3D11BaseFilterClass *bfilter_class = GST_D3D11_BASE_FILTER_CLASS (klass);
106 GstCaps *caps;
107
108 gobject_class->dispose = gst_d3d11_upload_dispose;
109
110 caps = gst_d3d11_get_updated_template_caps (&sink_template_caps);
111 gst_element_class_add_pad_template (element_class,
112 gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
113 gst_caps_unref (caps);
114
115 caps = gst_d3d11_get_updated_template_caps (&src_template_caps);
116 gst_element_class_add_pad_template (element_class,
117 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps));
118 gst_caps_unref (caps);
119
120 gst_element_class_set_static_metadata (element_class,
121 "Direct3D11 uploader", "Filter/Video",
122 "Uploads data into Direct3D11 texture memory",
123 "Seungha Yang <seungha.yang@navercorp.com>");
124
125 trans_class->passthrough_on_same_caps = TRUE;
126
127 trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_upload_stop);
128 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_d3d11_upload_sink_event);
129 trans_class->transform_caps =
130 GST_DEBUG_FUNCPTR (gst_d3d11_upload_transform_caps);
131 trans_class->propose_allocation =
132 GST_DEBUG_FUNCPTR (gst_d3d11_upload_propose_allocation);
133 trans_class->decide_allocation =
134 GST_DEBUG_FUNCPTR (gst_d3d11_upload_decide_allocation);
135 trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_upload_transform);
136
137 bfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_d3d11_upload_set_info);
138
139 GST_DEBUG_CATEGORY_INIT (gst_d3d11_upload_debug,
140 "d3d11upload", 0, "d3d11upload Element");
141 }
142
143 static void
gst_d3d11_upload_init(GstD3D11Upload * upload)144 gst_d3d11_upload_init (GstD3D11Upload * upload)
145 {
146 }
147
148 static void
gst_d3d11_upload_dispose(GObject * object)149 gst_d3d11_upload_dispose (GObject * object)
150 {
151 GstD3D11Upload *self = GST_D3D11_UPLOAD (object);
152
153 gst_clear_buffer (&self->staging_buffer);
154
155 G_OBJECT_CLASS (parent_class)->dispose (object);
156 }
157
158 static gboolean
gst_d3d11_upload_stop(GstBaseTransform * trans)159 gst_d3d11_upload_stop (GstBaseTransform * trans)
160 {
161 GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
162
163 gst_clear_buffer (&self->staging_buffer);
164
165 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (trans);
166 }
167
168 static gboolean
gst_d3d11_upload_sink_event(GstBaseTransform * trans,GstEvent * event)169 gst_d3d11_upload_sink_event (GstBaseTransform * trans, GstEvent * event)
170 {
171 GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
172
173 switch (GST_EVENT_TYPE (event)) {
174 case GST_EVENT_EOS:
175 /* We don't need to hold this staging buffer after eos */
176 gst_clear_buffer (&self->staging_buffer);
177 break;
178 default:
179 break;
180 }
181
182 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
183 }
184
185 static GstCaps *
_set_caps_features(const GstCaps * caps,const gchar * feature_name)186 _set_caps_features (const GstCaps * caps, const gchar * feature_name)
187 {
188 guint i, j, m, n;
189 GstCaps *tmp;
190 GstCapsFeatures *overlay_feature =
191 gst_caps_features_from_string
192 (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
193
194 tmp = gst_caps_new_empty ();
195
196 n = gst_caps_get_size (caps);
197 for (i = 0; i < n; i++) {
198 GstCapsFeatures *features, *orig_features;
199 GstStructure *s = gst_caps_get_structure (caps, i);
200
201 orig_features = gst_caps_get_features (caps, i);
202 features = gst_caps_features_new (feature_name, NULL);
203
204 if (gst_caps_features_is_any (orig_features)) {
205 gst_caps_append_structure_full (tmp, gst_structure_copy (s),
206 gst_caps_features_copy (features));
207
208 if (!gst_caps_features_contains (features,
209 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION))
210 gst_caps_features_add (features,
211 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
212 } else {
213 m = gst_caps_features_get_size (orig_features);
214 for (j = 0; j < m; j++) {
215 const gchar *feature = gst_caps_features_get_nth (orig_features, j);
216
217 /* if we already have the features */
218 if (gst_caps_features_contains (features, feature))
219 continue;
220
221 if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0)
222 continue;
223
224 if (gst_caps_features_contains (overlay_feature, feature)) {
225 gst_caps_features_add (features, feature);
226 }
227 }
228 }
229
230 gst_caps_append_structure_full (tmp, gst_structure_copy (s), features);
231 }
232
233 gst_caps_features_free (overlay_feature);
234
235 return tmp;
236 }
237
238 static GstCaps *
gst_d3d11_upload_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)239 gst_d3d11_upload_transform_caps (GstBaseTransform * trans,
240 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
241 {
242 GstCaps *result, *tmp;
243
244 GST_DEBUG_OBJECT (trans,
245 "Transforming caps %" GST_PTR_FORMAT " in direction %s", caps,
246 (direction == GST_PAD_SINK) ? "sink" : "src");
247
248 if (direction == GST_PAD_SINK) {
249 tmp = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY);
250 tmp = gst_caps_merge (gst_caps_ref (caps), tmp);
251 } else {
252 GstCaps *newcaps;
253 tmp = gst_caps_ref (caps);
254 newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
255 tmp = gst_caps_merge (tmp, newcaps);
256 }
257
258 if (filter) {
259 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
260 gst_caps_unref (tmp);
261 } else {
262 result = tmp;
263 }
264
265 GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, result);
266
267 return result;
268 }
269
270 static gboolean
gst_d3d11_upload_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)271 gst_d3d11_upload_propose_allocation (GstBaseTransform * trans,
272 GstQuery * decide_query, GstQuery * query)
273 {
274 GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
275 GstVideoInfo info;
276 GstBufferPool *pool;
277 GstCaps *caps;
278 guint size;
279
280 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
281 decide_query, query))
282 return FALSE;
283
284 /* passthrough, we're done */
285 if (decide_query == NULL)
286 return TRUE;
287
288 gst_query_parse_allocation (query, &caps, NULL);
289
290 if (caps == NULL)
291 return FALSE;
292
293 if (!gst_video_info_from_caps (&info, caps))
294 return FALSE;
295
296 if (gst_query_get_n_allocation_pools (query) == 0) {
297 GstCapsFeatures *features;
298 GstStructure *config;
299 gboolean is_d3d11 = FALSE;
300
301 features = gst_caps_get_features (caps, 0);
302
303 if (features && gst_caps_features_contains (features,
304 GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
305 GST_DEBUG_OBJECT (filter, "upstream support d3d11 memory");
306 pool = gst_d3d11_buffer_pool_new (filter->device);
307 is_d3d11 = TRUE;
308 } else {
309 pool = gst_video_buffer_pool_new ();
310 }
311
312 config = gst_buffer_pool_get_config (pool);
313
314 gst_buffer_pool_config_add_option (config,
315 GST_BUFFER_POOL_OPTION_VIDEO_META);
316
317 /* d3d11 pool does not support video alignment */
318 if (!is_d3d11) {
319 gst_buffer_pool_config_add_option (config,
320 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
321 }
322
323 size = GST_VIDEO_INFO_SIZE (&info);
324 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
325
326 if (!gst_buffer_pool_set_config (pool, config))
327 goto config_failed;
328
329 /* d3d11 buffer pool will update buffer size based on allocated texture,
330 * get size from config again */
331 if (is_d3d11) {
332 config = gst_buffer_pool_get_config (pool);
333 gst_buffer_pool_config_get_params (config,
334 nullptr, &size, nullptr, nullptr);
335 gst_structure_free (config);
336 }
337
338 gst_query_add_allocation_pool (query, pool, size, 0, 0);
339 gst_object_unref (pool);
340 }
341
342 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
343 gst_query_add_allocation_meta (query,
344 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
345
346 return TRUE;
347
348 /* ERRORS */
349 config_failed:
350 {
351 GST_ERROR_OBJECT (filter, "failed to set config");
352 gst_object_unref (pool);
353 return FALSE;
354 }
355 }
356
357 static gboolean
gst_d3d11_upload_decide_allocation(GstBaseTransform * trans,GstQuery * query)358 gst_d3d11_upload_decide_allocation (GstBaseTransform * trans, GstQuery * query)
359 {
360 GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
361 GstCaps *outcaps = NULL;
362 GstBufferPool *pool = NULL;
363 guint size, min, max;
364 GstStructure *config;
365 gboolean update_pool = FALSE;
366 GstVideoInfo vinfo;
367 const GstD3D11Format *d3d11_format;
368 GstD3D11AllocationParams *d3d11_params;
369 guint bind_flags = 0;
370 guint i;
371 DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
372 UINT supported = 0;
373 HRESULT hr;
374 ID3D11Device *device_handle;
375
376 gst_query_parse_allocation (query, &outcaps, NULL);
377
378 if (!outcaps)
379 return FALSE;
380
381 gst_video_info_from_caps (&vinfo, outcaps);
382
383 d3d11_format = gst_d3d11_device_format_from_gst (filter->device,
384 GST_VIDEO_INFO_FORMAT (&vinfo));
385 if (!d3d11_format) {
386 GST_ERROR_OBJECT (filter, "Unknown format caps %" GST_PTR_FORMAT, outcaps);
387 return FALSE;
388 }
389
390 if (d3d11_format->dxgi_format == DXGI_FORMAT_UNKNOWN) {
391 dxgi_format = d3d11_format->resource_format[0];
392 } else {
393 dxgi_format = d3d11_format->dxgi_format;
394 }
395
396 device_handle = gst_d3d11_device_get_device_handle (filter->device);
397 hr = device_handle->CheckFormatSupport (dxgi_format, &supported);
398 if (gst_d3d11_result (hr, filter->device)) {
399 if ((supported & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) ==
400 D3D11_FORMAT_SUPPORT_SHADER_SAMPLE) {
401 bind_flags |= D3D11_BIND_SHADER_RESOURCE;
402 }
403
404 if ((supported & D3D11_FORMAT_SUPPORT_RENDER_TARGET) ==
405 D3D11_FORMAT_SUPPORT_RENDER_TARGET) {
406 bind_flags |= D3D11_BIND_RENDER_TARGET;
407 }
408 }
409
410 if (gst_query_get_n_allocation_pools (query) > 0) {
411 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
412 if (pool) {
413 if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
414 gst_clear_object (&pool);
415 } else {
416 GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
417 if (dpool->device != filter->device)
418 gst_clear_object (&pool);
419 }
420 }
421
422 update_pool = TRUE;
423 } else {
424 size = GST_VIDEO_INFO_SIZE (&vinfo);
425 min = max = 0;
426 }
427
428 if (!pool) {
429 GST_DEBUG_OBJECT (trans, "create our pool");
430
431 pool = gst_d3d11_buffer_pool_new (filter->device);
432 }
433
434 config = gst_buffer_pool_get_config (pool);
435 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
436 gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
437
438 d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
439 if (!d3d11_params) {
440 d3d11_params = gst_d3d11_allocation_params_new (filter->device, &vinfo,
441 (GstD3D11AllocationFlags) 0, bind_flags);
442 } else {
443 /* Set bind flag */
444 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vinfo); i++) {
445 d3d11_params->desc[i].BindFlags |= bind_flags;
446 }
447 }
448
449 gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
450 gst_d3d11_allocation_params_free (d3d11_params);
451
452 gst_buffer_pool_set_config (pool, config);
453
454 /* d3d11 buffer pool will update buffer size based on allocated texture,
455 * get size from config again */
456 config = gst_buffer_pool_get_config (pool);
457 gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
458 gst_structure_free (config);
459
460 if (update_pool)
461 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
462 else
463 gst_query_add_allocation_pool (query, pool, size, min, max);
464
465 gst_object_unref (pool);
466
467 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
468 query);
469 }
470
471 static gboolean
gst_d3d11_upload_can_use_staging_buffer(GstD3D11Upload * self,GstBuffer * outbuf)472 gst_d3d11_upload_can_use_staging_buffer (GstD3D11Upload * self,
473 GstBuffer * outbuf)
474 {
475 GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (self);
476 ID3D11Device *device_handle =
477 gst_d3d11_device_get_device_handle (filter->device);
478
479 if (!gst_d3d11_buffer_can_access_device (outbuf, device_handle))
480 return FALSE;
481
482 if (self->staging_buffer)
483 return TRUE;
484
485 self->staging_buffer = gst_d3d11_allocate_staging_buffer_for (outbuf,
486 &filter->out_info, TRUE);
487
488 if (!self->staging_buffer) {
489 GST_WARNING_OBJECT (self, "Couldn't allocate staging buffer");
490 return FALSE;
491 }
492
493 return TRUE;
494 }
495
496 static GstFlowReturn
gst_d3d11_upload_transform(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)497 gst_d3d11_upload_transform (GstBaseTransform * trans, GstBuffer * inbuf,
498 GstBuffer * outbuf)
499 {
500 GstD3D11BaseFilter *filter = GST_D3D11_BASE_FILTER (trans);
501 GstD3D11Upload *self = GST_D3D11_UPLOAD (trans);
502 GstVideoFrame in_frame, out_frame;
503 GstFlowReturn ret = GST_FLOW_OK;
504 gboolean use_staging_buf;
505 GstBuffer *target_outbuf = outbuf;
506 guint i;
507
508 use_staging_buf = gst_d3d11_upload_can_use_staging_buffer (self, outbuf);
509
510 if (use_staging_buf) {
511 GST_TRACE_OBJECT (self, "Copy input buffer to staging buffer");
512 target_outbuf = self->staging_buffer;
513 }
514
515 if (!gst_video_frame_map (&in_frame, &filter->in_info, inbuf,
516 (GstMapFlags) (GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF)))
517 goto invalid_buffer;
518
519 if (!gst_video_frame_map (&out_frame, &filter->out_info, target_outbuf,
520 (GstMapFlags) (GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))) {
521 gst_video_frame_unmap (&in_frame);
522 goto invalid_buffer;
523 }
524
525 for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&in_frame); i++) {
526 if (!gst_video_frame_copy_plane (&out_frame, &in_frame, i)) {
527 GST_ERROR_OBJECT (filter, "Couldn't copy plane %d", i);
528 ret = GST_FLOW_ERROR;
529 break;
530 }
531 }
532
533 gst_video_frame_unmap (&out_frame);
534 gst_video_frame_unmap (&in_frame);
535
536 /* Copy staging texture to d3d11 texture */
537 if (use_staging_buf) {
538 if (!gst_d3d11_buffer_copy_into (outbuf,
539 self->staging_buffer, &filter->out_info)) {
540 GST_ERROR_OBJECT (self, "Cannot copy staging texture into texture");
541 return GST_FLOW_ERROR;
542 }
543 }
544
545 return ret;
546
547 /* ERRORS */
548 invalid_buffer:
549 {
550 GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
551 ("invalid video buffer received"));
552 return GST_FLOW_ERROR;
553 }
554 }
555
556 static gboolean
gst_d3d11_upload_set_info(GstD3D11BaseFilter * filter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)557 gst_d3d11_upload_set_info (GstD3D11BaseFilter * filter,
558 GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
559 GstVideoInfo * out_info)
560 {
561 GstD3D11Upload *self = GST_D3D11_UPLOAD (filter);
562
563 gst_clear_buffer (&self->staging_buffer);
564
565 return TRUE;
566 }
567