69c854d77d7ad889b599b3795ec23a277af88116
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / d3d11 / gstd3d11deinterlace.cpp
1 /* GStreamer
2  * Copyright (C) 2021 Seungha Yang <seungha@centricular.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-d3d11deinterlaceelement
22  * @title: d3d11deinterlaceelement
23  *
24  * Deinterlacing interlaced video frames to progressive video frames by using
25  * ID3D11VideoProcessor API. Depending on the hardware it runs on,
26  * this element will only support a very limited set of video formats.
27  * Use #d3d11deinterlace instead, which will take care of conversion.
28  *
29  * Since: 1.20
30  *
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36
37 #include <gst/video/video.h>
38 #include <gst/base/gstbasetransform.h>
39
40 #include "gstd3d11deinterlace.h"
41 #include "gstd3d11pluginutils.h"
42 #include <wrl.h>
43 #include <string.h>
44
45 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_deinterlace_debug);
46 #define GST_CAT_DEFAULT gst_d3d11_deinterlace_debug
47
48 /* *INDENT-OFF* */
49 using namespace Microsoft::WRL;
50 /* *INDENT-ON* */
51
52 /* Deinterlacing Methods:
53  * Direct3D11 provides Blend, Bob, Adaptive, Motion Compensation, and
54  * Inverse Telecine methods. But depending on video processor device,
55  * some of method might not be supported.
56  * - Blend: the two fields of a interlaced frame are blended into a single
57  *   progressive frame. Output rate will be half of input (e.g., 60i -> 30p)
58  *   but due to the way of framerate signalling of GStreamer, that is, it uses
59  *   frame rate, not field rate for interlaced stream, in/output framerate
60  *   of caps will be identical.
61  * - Bob: missing field lines are interpolated from the lines above and below.
62  *   Output rate will be the same as that of input (e.g., 60i -> 60p).
63  *   In order words, video processor will generate two frames from two field
64  *   of a intelaced frame.
65  * - Adaptive, Motion Compensation: future and past frames are used for
66  *   reference frame for deinterlacing process. User should provide sufficent
67  *   number of reference frames, otherwise processor device will fallback to
68  *   Bob method.
69  *
70  * Direct3D11 doesn't provide a method for explicit deinterlacing method
71  * selection. Instead, it could be done indirectly.
72  * - Blend: sets output rate as half via VideoProcessorSetStreamOutputRate().
73  * - Bob: sets output rate as normal. And performs VideoProcessorBlt() twice per
74  *   a interlaced frame. D3D11_VIDEO_PROCESSOR_STREAM::OutputIndex needs to be
75  *   incremented per field (e.g., OutputIndex = 0 for the first field,
76  *   and 1 for the second field).
77  * - Adaptive, Motion Compensation: in addition to the requirement of Bob,
78  *   user should provide reference frames via
79  *   D3D11_VIDEO_PROCESSOR_STREAM::ppPastSurfaces and
80  *   D3D11_VIDEO_PROCESSOR_STREAM::ppFutureSurfaces
81  */
82
83 typedef enum
84 {
85   GST_D3D11_DEINTERLACE_METHOD_BLEND =
86       D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND,
87   GST_D3D11_DEINTERLACE_METHOD_BOB =
88       D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB,
89   GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE =
90       D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE,
91   GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION =
92       D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION,
93
94   /* TODO: INVERSE_TELECINE */
95 } GstD3D11DeinterlaceMethod;
96
97 /**
98  * GstD3D11DeinterlaceMethod:
99  *
100  * Deinterlacing method
101  *
102  * Since: 1.20
103  */
104 #define GST_TYPE_D3D11_DEINTERLACE_METHOD (gst_d3d11_deinterlace_method_type())
105
106 static GType
107 gst_d3d11_deinterlace_method_type (void)
108 {
109   static GType method_type = 0;
110
111   GST_D3D11_CALL_ONCE_BEGIN {
112     static const GFlagsValue method_types[] = {
113       {GST_D3D11_DEINTERLACE_METHOD_BLEND,
114           "Blend: Blending top/bottom field pictures into one frame. "
115             "Framerate will be preserved (e.g., 60i -> 30p)", "blend"},
116       {GST_D3D11_DEINTERLACE_METHOD_BOB,
117           "Bob: Interpolating missing lines by using the adjacent lines. "
118             "Framerate will be doubled (e,g, 60i -> 60p)", "bob"},
119       {GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE,
120             "Adaptive: Interpolating missing lines by using spatial/temporal references. "
121             "Framerate will be doubled (e,g, 60i -> 60p)",
122           "adaptive"},
123       {GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION,
124           "Motion Compensation: Recreating missing lines by using motion vector. "
125             "Framerate will be doubled (e,g, 60i -> 60p)", "mocomp"},
126       {0, nullptr, nullptr},
127     };
128
129     method_type = g_flags_register_static ("GstD3D11DeinterlaceMethod",
130         method_types);
131   } GST_D3D11_CALL_ONCE_END;
132
133   return method_type;
134 }
135
136 typedef struct
137 {
138   GstD3D11DeinterlaceMethod supported_methods;
139   GstD3D11DeinterlaceMethod default_method;
140
141   guint max_past_frames;
142   guint max_future_frames;
143 } GstD3D11DeinterlaceDeviceCaps;
144
145 typedef struct
146 {
147   GType deinterlace_type;
148
149   GstCaps *sink_caps;
150   GstCaps *src_caps;
151   guint adapter;
152   guint device_id;
153   guint vendor_id;
154   gchar *description;
155
156   GstD3D11DeinterlaceDeviceCaps device_caps;
157
158   guint ref_count;
159 } GstD3D11DeinterlaceClassData;
160
161 static GstD3D11DeinterlaceClassData *
162 gst_d3d11_deinterlace_class_data_new (void)
163 {
164   GstD3D11DeinterlaceClassData *self = g_new0 (GstD3D11DeinterlaceClassData, 1);
165
166   self->ref_count = 1;
167
168   return self;
169 }
170
171 static GstD3D11DeinterlaceClassData *
172 gst_d3d11_deinterlace_class_data_ref (GstD3D11DeinterlaceClassData * data)
173 {
174   g_assert (data != NULL);
175
176   g_atomic_int_add (&data->ref_count, 1);
177
178   return data;
179 }
180
181 static void
182 gst_d3d11_deinterlace_class_data_unref (GstD3D11DeinterlaceClassData * data)
183 {
184   g_assert (data != NULL);
185
186   if (g_atomic_int_dec_and_test (&data->ref_count)) {
187     gst_clear_caps (&data->sink_caps);
188     gst_clear_caps (&data->src_caps);
189     g_free (data->description);
190     g_free (data);
191   }
192 }
193
194 enum
195 {
196   PROP_0,
197   PROP_ADAPTER,
198   PROP_DEVICE_ID,
199   PROP_VENDOR_ID,
200   PROP_METHOD,
201   PROP_SUPPORTED_METHODS,
202 };
203
204 /* hardcoded maximum queue size for each past/future frame queue */
205 #define MAX_NUM_REFERENCES 2
206
207 typedef struct _GstD3D11Deinterlace
208 {
209   GstBaseTransform parent;
210
211   GstVideoInfo in_info;
212   GstVideoInfo out_info;
213   /* Calculated buffer duration by using upstream framerate */
214   GstClockTime default_buffer_duration;
215
216   GstD3D11Device *device;
217
218   ID3D11VideoDevice *video_device;
219   ID3D11VideoContext *video_context;
220   ID3D11VideoProcessorEnumerator *video_enum;
221   ID3D11VideoProcessor *video_proc;
222
223   GstD3D11DeinterlaceMethod method;
224
225   CRITICAL_SECTION lock;
226   GQueue past_frame_queue;
227   GQueue future_frame_queue;
228   GstBuffer *to_process;
229
230   guint max_past_frames;
231   guint max_future_frames;
232
233   /* D3D11_VIDEO_PROCESSOR_STREAM::InputFrameOrField */
234   guint input_index;
235
236   /* Clear/Update per submit_input_buffer() */
237   guint num_output_per_input;
238   guint num_transformed;
239   gboolean first_output;
240
241   GstBufferPool *fallback_in_pool;
242   GstBufferPool *fallback_out_pool;
243 } GstD3D11Deinterlace;
244
245 typedef struct _GstD3D11DeinterlaceClass
246 {
247   GstBaseTransformClass parent_class;
248
249   guint adapter;
250   guint device_id;
251   guint vendor_id;
252
253   GstD3D11DeinterlaceDeviceCaps device_caps;
254 } GstD3D11DeinterlaceClass;
255
256 static GstElementClass *parent_class = NULL;
257
258 #define GST_D3D11_DEINTERLACE(object) ((GstD3D11Deinterlace *) (object))
259 #define GST_D3D11_DEINTERLACE_GET_CLASS(object) \
260     (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object), \
261     GstD3D11DeinterlaceClass))
262
263 static gboolean
264 gst_d3d11_deinterlace_update_method (GstD3D11Deinterlace * self);
265 static void gst_d3d11_deinterlace_reset (GstD3D11Deinterlace * self);
266 static GstFlowReturn gst_d3d11_deinterlace_drain (GstD3D11Deinterlace * self);
267
268 /* GObjectClass vfunc */
269 static void gst_d3d11_deinterlace_get_property (GObject * object,
270     guint prop_id, GValue * value, GParamSpec * pspec);
271 static void gst_d3d11_deinterlace_set_property (GObject * object, guint prop_id,
272     const GValue * value, GParamSpec * pspec);
273 static void gst_d3d11_deinterlace_finalize (GObject * object);
274
275 /* GstElementClass vfunc */
276 static void gst_d3d11_deinterlace_set_context (GstElement * element,
277     GstContext * context);
278
279 /* GstBaseTransformClass vfunc */
280 static gboolean gst_d3d11_deinterlace_start (GstBaseTransform * trans);
281 static gboolean gst_d3d11_deinterlace_stop (GstBaseTransform * trans);
282 static gboolean gst_d3d11_deinterlace_query (GstBaseTransform * trans,
283     GstPadDirection direction, GstQuery * query);
284 static GstCaps *gst_d3d11_deinterlace_transform_caps (GstBaseTransform * trans,
285     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
286 static GstCaps *gst_d3d11_deinterlace_fixate_caps (GstBaseTransform * trans,
287     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
288 static gboolean
289 gst_d3d11_deinterlace_propose_allocation (GstBaseTransform * trans,
290     GstQuery * decide_query, GstQuery * query);
291 static gboolean
292 gst_d3d11_deinterlace_decide_allocation (GstBaseTransform * trans,
293     GstQuery * query);
294 static gboolean gst_d3d11_deinterlace_set_caps (GstBaseTransform * trans,
295     GstCaps * incaps, GstCaps * outcaps);
296 static GstFlowReturn
297 gst_d3d11_deinterlace_submit_input_buffer (GstBaseTransform * trans,
298     gboolean is_discont, GstBuffer * input);
299 static GstFlowReturn
300 gst_d3d11_deinterlace_generate_output (GstBaseTransform * trans,
301     GstBuffer ** outbuf);
302 static GstFlowReturn
303 gst_d3d11_deinterlace_transform (GstBaseTransform * trans, GstBuffer * inbuf,
304     GstBuffer * outbuf);
305 static gboolean gst_d3d11_deinterlace_sink_event (GstBaseTransform * trans,
306     GstEvent * event);
307 static void gst_d3d11_deinterlace_before_transform (GstBaseTransform * trans,
308     GstBuffer * buffer);
309
310 static void
311 gst_d3d11_deinterlace_class_init (GstD3D11DeinterlaceClass * klass,
312     gpointer data)
313 {
314   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
315   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
316   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
317   GstD3D11DeinterlaceClassData *cdata = (GstD3D11DeinterlaceClassData *) data;
318   gchar *long_name;
319
320   parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
321
322   gobject_class->get_property = gst_d3d11_deinterlace_get_property;
323   gobject_class->set_property = gst_d3d11_deinterlace_set_property;
324   gobject_class->finalize = gst_d3d11_deinterlace_finalize;
325
326   g_object_class_install_property (gobject_class, PROP_ADAPTER,
327       g_param_spec_uint ("adapter", "Adapter",
328           "DXGI Adapter index for creating device",
329           0, G_MAXUINT32, cdata->adapter,
330           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
331   g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
332       g_param_spec_uint ("device-id", "Device Id",
333           "DXGI Device ID", 0, G_MAXUINT32, 0,
334           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
335   g_object_class_install_property (gobject_class, PROP_VENDOR_ID,
336       g_param_spec_uint ("vendor-id", "Vendor Id",
337           "DXGI Vendor ID", 0, G_MAXUINT32, 0,
338           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
339   g_object_class_install_property (gobject_class, PROP_METHOD,
340       g_param_spec_flags ("method", "Method",
341           "Deinterlace Method. Use can set multiple methods as a flagset "
342           "and element will select one of method automatically. "
343           "If deinterlacing device failed to deinterlace with given mode, "
344           "fallback might happen by the device",
345           GST_TYPE_D3D11_DEINTERLACE_METHOD, cdata->device_caps.default_method,
346           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
347               GST_PARAM_MUTABLE_READY)));
348   g_object_class_install_property (gobject_class, PROP_SUPPORTED_METHODS,
349       g_param_spec_flags ("supported-methods", "Supported Methods",
350           "Set of supported deinterlace methods by device",
351           GST_TYPE_D3D11_DEINTERLACE_METHOD,
352           cdata->device_caps.supported_methods,
353           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
354
355   element_class->set_context =
356       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_set_context);
357
358   long_name = g_strdup_printf ("Direct3D11 %s Deinterlacer",
359       cdata->description);
360   gst_element_class_set_metadata (element_class, long_name,
361       "Filter/Effect/Video/Deinterlace/Hardware",
362       "A Direct3D11 based deinterlacer",
363       "Seungha Yang <seungha@centricular.com>");
364   g_free (long_name);
365
366   gst_element_class_add_pad_template (element_class,
367       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
368           cdata->sink_caps));
369   gst_element_class_add_pad_template (element_class,
370       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
371           cdata->src_caps));
372
373   trans_class->passthrough_on_same_caps = TRUE;
374
375   trans_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_start);
376   trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_stop);
377   trans_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_query);
378   trans_class->transform_caps =
379       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_transform_caps);
380   trans_class->fixate_caps =
381       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_fixate_caps);
382   trans_class->propose_allocation =
383       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_propose_allocation);
384   trans_class->decide_allocation =
385       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_decide_allocation);
386   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_set_caps);
387   trans_class->submit_input_buffer =
388       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_submit_input_buffer);
389   trans_class->generate_output =
390       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_generate_output);
391   trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_transform);
392   trans_class->sink_event =
393       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_sink_event);
394   trans_class->before_transform =
395       GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_before_transform);
396
397   klass->adapter = cdata->adapter;
398   klass->device_id = cdata->device_id;
399   klass->vendor_id = cdata->vendor_id;
400   klass->device_caps = cdata->device_caps;
401
402   gst_d3d11_deinterlace_class_data_unref (cdata);
403
404   gst_type_mark_as_plugin_api (GST_TYPE_D3D11_DEINTERLACE_METHOD,
405       (GstPluginAPIFlags) 0);
406 }
407
408 static void
409 gst_d3d11_deinterlace_init (GstD3D11Deinterlace * self)
410 {
411   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
412
413   self->method = klass->device_caps.default_method;
414   self->default_buffer_duration = GST_CLOCK_TIME_NONE;
415   gst_d3d11_deinterlace_update_method (self);
416
417   g_queue_init (&self->past_frame_queue);
418   g_queue_init (&self->future_frame_queue);
419   InitializeCriticalSection (&self->lock);
420 }
421
422 static void
423 gst_d3d11_deinterlace_get_property (GObject * object, guint prop_id,
424     GValue * value, GParamSpec * pspec)
425 {
426   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
427   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (object);
428
429   switch (prop_id) {
430     case PROP_ADAPTER:
431       g_value_set_uint (value, klass->adapter);
432       break;
433     case PROP_DEVICE_ID:
434       g_value_set_uint (value, klass->device_id);
435       break;
436     case PROP_VENDOR_ID:
437       g_value_set_uint (value, klass->vendor_id);
438       break;
439     case PROP_METHOD:
440       g_value_set_flags (value, self->method);
441       break;
442     case PROP_SUPPORTED_METHODS:
443       g_value_set_flags (value, klass->device_caps.supported_methods);
444       break;
445     default:
446       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447       break;
448   }
449 }
450
451 static gboolean
452 gst_d3d11_deinterlace_update_method (GstD3D11Deinterlace * self)
453 {
454   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
455   GstD3D11DeinterlaceMethod requested_method = self->method;
456   gboolean updated = TRUE;
457
458   /* Verify whether requested method is supported */
459   if ((self->method & klass->device_caps.supported_methods) == 0) {
460 #ifndef GST_DISABLE_GST_DEBUG
461     gchar *supported, *requested;
462
463     supported = g_flags_to_string (GST_TYPE_D3D11_DEINTERLACE_METHOD,
464         klass->device_caps.supported_methods);
465     requested = g_flags_to_string (GST_TYPE_D3D11_DEINTERLACE_METHOD,
466         klass->device_caps.supported_methods);
467
468     GST_WARNING_OBJECT (self,
469         "Requested method %s is not supported (supported: %s)",
470         requested, supported);
471
472     g_free (supported);
473     g_free (requested);
474 #endif
475
476     self->method = klass->device_caps.default_method;
477
478     goto done;
479   }
480
481   /* Drop not supported methods */
482   self->method = (GstD3D11DeinterlaceMethod)
483       (klass->device_caps.supported_methods & self->method);
484
485   /* Single method was requested? */
486   if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND ||
487       self->method == GST_D3D11_DEINTERLACE_METHOD_BOB ||
488       self->method == GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE ||
489       self->method == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
490     if (self->method == requested_method)
491       updated = FALSE;
492   } else {
493     /* Pick single method from requested */
494     if ((self->method & GST_D3D11_DEINTERLACE_METHOD_BOB) ==
495         GST_D3D11_DEINTERLACE_METHOD_BOB) {
496       self->method = GST_D3D11_DEINTERLACE_METHOD_BOB;
497     } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE) ==
498         GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE) {
499       self->method = GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE;
500     } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION)
501         == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
502       self->method = GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION;
503     } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_BLEND) ==
504         GST_D3D11_DEINTERLACE_METHOD_BLEND) {
505       self->method = GST_D3D11_DEINTERLACE_METHOD_BLEND;
506     } else {
507       self->method = klass->device_caps.default_method;
508       g_assert_not_reached ();
509     }
510   }
511
512 done:
513   if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND) {
514     /* Both methods don't use reference frame for deinterlacing */
515     self->max_past_frames = self->max_future_frames = 0;
516   } else if (self->method == GST_D3D11_DEINTERLACE_METHOD_BOB) {
517     /* To calculate timestamp and duration of output fraems, we will hold one
518      * future frame even though processor device will not use reference */
519     self->max_past_frames = 0;
520     self->max_future_frames = 1;
521   } else {
522     /* FIXME: how many frames should be allowed? also, this needs to be
523      * configurable */
524     self->max_past_frames = MIN (klass->device_caps.max_past_frames,
525         MAX_NUM_REFERENCES);
526
527     /* Likewise Bob, we need at least one future frame for timestamp/duration
528      * calculation */
529     self->max_future_frames =
530         MAX (MIN (klass->device_caps.max_future_frames, MAX_NUM_REFERENCES), 1);
531   }
532
533   return updated;
534 }
535
536 static void
537 gst_d3d11_deinterlace_set_property (GObject * object, guint prop_id,
538     const GValue * value, GParamSpec * pspec)
539 {
540   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
541
542   switch (prop_id) {
543     case PROP_METHOD:{
544       gboolean notify_update = FALSE;
545
546       GST_OBJECT_LOCK (self);
547       self->method = (GstD3D11DeinterlaceMethod) g_value_get_flags (value);
548       notify_update = gst_d3d11_deinterlace_update_method (self);
549       GST_OBJECT_UNLOCK (self);
550
551       if (notify_update)
552         g_object_notify (object, "method");
553       break;
554     }
555     default:
556       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
557       break;
558   }
559 }
560
561 static void
562 gst_d3d11_deinterlace_finalize (GObject * object)
563 {
564   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
565
566   DeleteCriticalSection (&self->lock);
567
568   G_OBJECT_CLASS (parent_class)->finalize (object);
569 }
570
571 static void
572 gst_d3d11_deinterlace_set_context (GstElement * element, GstContext * context)
573 {
574   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (element);
575   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
576
577   gst_d3d11_handle_set_context (element, context, klass->adapter,
578       &self->device);
579
580   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
581 }
582
583 static gboolean
584 gst_d3d11_deinterlace_open (GstD3D11Deinterlace * self)
585 {
586   ID3D11VideoDevice *video_device;
587   ID3D11VideoContext *video_context;
588
589   video_device = gst_d3d11_device_get_video_device_handle (self->device);
590   if (!video_device) {
591     GST_ERROR_OBJECT (self, "ID3D11VideoDevice is not availale");
592     return FALSE;
593   }
594
595   video_context = gst_d3d11_device_get_video_context_handle (self->device);
596   if (!video_context) {
597     GST_ERROR_OBJECT (self, "ID3D11VideoContext is not available");
598     return FALSE;
599   }
600
601   self->video_device = video_device;
602   video_device->AddRef ();
603
604   self->video_context = video_context;
605   video_context->AddRef ();
606
607   return TRUE;
608 }
609
610 /* Must be called with lock taken */
611 static void
612 gst_d3d11_deinterlace_reset_history (GstD3D11Deinterlace * self)
613 {
614   self->input_index = 0;
615   self->num_output_per_input = 1;
616   self->num_transformed = 0;
617   self->first_output = TRUE;
618
619   g_queue_clear_full (&self->past_frame_queue,
620       (GDestroyNotify) gst_buffer_unref);
621   g_queue_clear_full (&self->future_frame_queue,
622       (GDestroyNotify) gst_buffer_unref);
623   gst_clear_buffer (&self->to_process);
624 }
625
626 static void
627 gst_d3d11_deinterlace_reset (GstD3D11Deinterlace * self)
628 {
629   GstD3D11CSLockGuard lk (&self->lock);
630
631   if (self->fallback_in_pool) {
632     gst_buffer_pool_set_active (self->fallback_in_pool, FALSE);
633     gst_object_unref (self->fallback_in_pool);
634     self->fallback_in_pool = NULL;
635   }
636
637   if (self->fallback_out_pool) {
638     gst_buffer_pool_set_active (self->fallback_out_pool, FALSE);
639     gst_object_unref (self->fallback_out_pool);
640     self->fallback_out_pool = NULL;
641   }
642
643   GST_D3D11_CLEAR_COM (self->video_enum);
644   GST_D3D11_CLEAR_COM (self->video_proc);
645
646   gst_d3d11_deinterlace_reset_history (self);
647   self->default_buffer_duration = GST_CLOCK_TIME_NONE;
648 }
649
650 static void
651 gst_d3d11_deinterlace_close (GstD3D11Deinterlace * self)
652 {
653   gst_d3d11_deinterlace_reset (self);
654
655   GST_D3D11_CLEAR_COM (self->video_device);
656   GST_D3D11_CLEAR_COM (self->video_context);
657
658   gst_clear_object (&self->device);
659 }
660
661 static gboolean
662 gst_d3d11_deinterlace_start (GstBaseTransform * trans)
663 {
664   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
665   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
666
667   if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), klass->adapter,
668           &self->device)) {
669     GST_ERROR_OBJECT (self, "Couldn't create d3d11device");
670     return FALSE;
671   }
672
673   if (!gst_d3d11_deinterlace_open (self)) {
674     GST_ERROR_OBJECT (self, "Couldn't open video device");
675     gst_d3d11_deinterlace_close (self);
676     return FALSE;
677   }
678
679   return TRUE;
680 }
681
682 static gboolean
683 gst_d3d11_deinterlace_stop (GstBaseTransform * trans)
684 {
685   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
686
687   gst_d3d11_deinterlace_close (self);
688
689   return TRUE;
690 }
691
692 static gboolean
693 gst_d3d11_deinterlace_query (GstBaseTransform * trans,
694     GstPadDirection direction, GstQuery * query)
695 {
696   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
697
698   switch (GST_QUERY_TYPE (query)) {
699     case GST_QUERY_CONTEXT:
700       if (gst_d3d11_handle_context_query (GST_ELEMENT_CAST (self),
701               query, self->device)) {
702         return TRUE;
703       }
704       break;
705     default:
706       break;
707   }
708
709   return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
710       query);
711 }
712
713 static GstCaps *
714 gst_d3d11_deinterlace_remove_interlace_info (GstCaps * caps,
715     gboolean remove_framerate)
716 {
717   GstStructure *st;
718   GstCapsFeatures *f;
719   gint i, n;
720   GstCaps *res;
721   GstCapsFeatures *feature =
722       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY);
723
724   res = gst_caps_new_empty ();
725
726   n = gst_caps_get_size (caps);
727   for (i = 0; i < n; i++) {
728     st = gst_caps_get_structure (caps, i);
729     f = gst_caps_get_features (caps, i);
730
731     /* If this is already expressed by the existing caps
732      * skip this structure */
733     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
734       continue;
735
736     st = gst_structure_copy (st);
737     /* Only remove format info for the cases when we can actually convert */
738     if (!gst_caps_features_is_any (f)
739         && gst_caps_features_is_equal (f, feature)) {
740       if (remove_framerate) {
741         gst_structure_remove_fields (st, "interlace-mode", "field-order",
742             "framerate", NULL);
743       } else {
744         gst_structure_remove_fields (st, "interlace-mode", "field-order", NULL);
745       }
746     }
747
748     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
749   }
750
751   gst_caps_features_free (feature);
752
753   return res;
754 }
755
756 static GstCaps *
757 gst_d3d11_deinterlace_transform_caps (GstBaseTransform * trans,
758     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
759 {
760   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
761   GstCaps *tmp, *tmp2;
762   GstCaps *result;
763
764   /* Get all possible caps that we can transform to */
765   tmp = gst_d3d11_deinterlace_remove_interlace_info (caps,
766       /* Non-blend mode will double framerate */
767       self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND);
768
769   if (filter) {
770     tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
771     gst_caps_unref (tmp);
772     tmp = tmp2;
773   }
774
775   result = tmp;
776
777   GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
778       GST_PTR_FORMAT, caps, result);
779
780   return result;
781 }
782
783 static GstCaps *
784 gst_d3d11_deinterlace_fixate_caps (GstBaseTransform * trans,
785     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
786 {
787   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
788   GstStructure *s;
789   GstCaps *tmp;
790   gint fps_n, fps_d;
791   GstVideoInfo info;
792   const gchar *interlace_mode;
793
794   othercaps = gst_caps_truncate (othercaps);
795   othercaps = gst_caps_make_writable (othercaps);
796
797   if (direction == GST_PAD_SRC)
798     return gst_caps_fixate (othercaps);
799
800   tmp = gst_caps_copy (caps);
801   tmp = gst_caps_fixate (tmp);
802
803   if (!gst_video_info_from_caps (&info, tmp)) {
804     GST_WARNING_OBJECT (self, "Invalid caps %" GST_PTR_FORMAT, caps);
805     gst_caps_unref (tmp);
806
807     return gst_caps_fixate (othercaps);
808   }
809
810   s = gst_caps_get_structure (tmp, 0);
811   if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
812     /* for non-blend method, output framerate will be doubled */
813     if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND &&
814         GST_VIDEO_INFO_IS_INTERLACED (&info)) {
815       fps_n *= 2;
816     }
817
818     gst_caps_set_simple (othercaps,
819         "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
820   }
821
822   interlace_mode = gst_structure_get_string (s, "interlace-mode");
823   if (g_strcmp0 ("progressive", interlace_mode) == 0) {
824     /* Just forward interlace-mode=progressive.
825      * By this way, basetransform will enable passthrough for non-interlaced
826      * stream*/
827     gst_caps_set_simple (othercaps,
828         "interlace-mode", G_TYPE_STRING, "progressive", NULL);
829   }
830
831   gst_caps_unref (tmp);
832
833   return gst_caps_fixate (othercaps);
834 }
835
836 static gboolean
837 gst_d3d11_deinterlace_propose_allocation (GstBaseTransform * trans,
838     GstQuery * decide_query, GstQuery * query)
839 {
840   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
841   GstVideoInfo info;
842   GstBufferPool *pool = NULL;
843   GstCaps *caps;
844   guint n_pools, i;
845   GstStructure *config;
846   guint size;
847   GstD3D11AllocationParams *d3d11_params;
848   guint min_buffers = 0;
849
850   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
851           decide_query, query))
852     return FALSE;
853
854   /* passthrough, we're done */
855   if (decide_query == NULL)
856     return TRUE;
857
858   gst_query_parse_allocation (query, &caps, NULL);
859
860   if (caps == NULL)
861     return FALSE;
862
863   if (!gst_video_info_from_caps (&info, caps))
864     return FALSE;
865
866   n_pools = gst_query_get_n_allocation_pools (query);
867   for (i = 0; i < n_pools; i++) {
868     gst_query_parse_nth_allocation_pool (query, i, &pool, NULL, NULL, NULL);
869     if (pool) {
870       if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
871         gst_clear_object (&pool);
872       } else {
873         GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
874         if (dpool->device != self->device)
875           gst_clear_object (&pool);
876       }
877     }
878   }
879
880   if (!pool)
881     pool = gst_d3d11_buffer_pool_new (self->device);
882
883   config = gst_buffer_pool_get_config (pool);
884   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
885
886   d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
887   if (!d3d11_params) {
888     d3d11_params = gst_d3d11_allocation_params_new (self->device, &info,
889         GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_RENDER_TARGET, 0);
890   } else {
891     d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
892   }
893
894   gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
895   gst_d3d11_allocation_params_free (d3d11_params);
896
897   if (self->method == GST_D3D11_DEINTERLACE_METHOD_BOB) {
898     /* For non-blend methods, we will produce two progressive frames from
899      * a single interlaced frame. To determine timestamp and duration,
900      * we might need to hold one past frame if buffer duration is unknown */
901     min_buffers = 2;
902   } else if (self->method == GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE ||
903       self->method == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
904     /* For advanced deinterlacing methods, we will hold more frame so that
905      * device can use them as reference frames */
906
907     min_buffers += self->max_past_frames;
908     min_buffers += self->max_future_frames;
909     /* And one for current frame */
910     min_buffers++;
911
912     /* we will hold at least one frame for timestamp/duration calculation */
913     min_buffers = MAX (min_buffers, 2);
914   }
915
916   /* size will be updated by d3d11 buffer pool */
917   gst_buffer_pool_config_set_params (config, caps, 0, min_buffers, 0);
918
919   if (!gst_buffer_pool_set_config (pool, config))
920     goto config_failed;
921
922   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
923   gst_query_add_allocation_meta (query,
924       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
925
926   /* d3d11 buffer pool will update buffer size based on allocated texture,
927    * get size from config again */
928   config = gst_buffer_pool_get_config (pool);
929   gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
930   gst_structure_free (config);
931
932   gst_query_add_allocation_pool (query, pool, size, min_buffers, 0);
933
934   gst_object_unref (pool);
935
936   return TRUE;
937
938   /* ERRORS */
939 config_failed:
940   {
941     GST_ERROR_OBJECT (self, "failed to set config");
942     gst_object_unref (pool);
943     return FALSE;
944   }
945 }
946
947 static gboolean
948 gst_d3d11_deinterlace_decide_allocation (GstBaseTransform * trans,
949     GstQuery * query)
950 {
951   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
952   GstCaps *outcaps = NULL;
953   GstBufferPool *pool = NULL;
954   guint size, min = 0, max = 0;
955   GstStructure *config;
956   GstD3D11AllocationParams *d3d11_params;
957   gboolean update_pool = FALSE;
958   GstVideoInfo info;
959
960   gst_query_parse_allocation (query, &outcaps, NULL);
961
962   if (!outcaps)
963     return FALSE;
964
965   if (!gst_video_info_from_caps (&info, outcaps))
966     return FALSE;
967
968   size = GST_VIDEO_INFO_SIZE (&info);
969
970   if (gst_query_get_n_allocation_pools (query) > 0) {
971     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
972     if (pool) {
973       if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
974         gst_clear_object (&pool);
975       } else {
976         GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
977         if (dpool->device != self->device)
978           gst_clear_object (&pool);
979       }
980     }
981
982     update_pool = TRUE;
983   }
984
985   if (!pool)
986     pool = gst_d3d11_buffer_pool_new (self->device);
987
988   config = gst_buffer_pool_get_config (pool);
989   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
990
991   d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
992   if (!d3d11_params) {
993     d3d11_params = gst_d3d11_allocation_params_new (self->device, &info,
994         GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_RENDER_TARGET, 0);
995   } else {
996     d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
997   }
998
999   gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
1000   gst_d3d11_allocation_params_free (d3d11_params);
1001
1002   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
1003   gst_buffer_pool_set_config (pool, config);
1004
1005   /* d3d11 buffer pool will update buffer size based on allocated texture,
1006    * get size from config again */
1007   config = gst_buffer_pool_get_config (pool);
1008   gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
1009   gst_structure_free (config);
1010
1011   if (update_pool)
1012     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1013   else
1014     gst_query_add_allocation_pool (query, pool, size, min, max);
1015
1016   gst_object_unref (pool);
1017
1018   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
1019       query);
1020 }
1021
1022 static gboolean
1023 gst_d3d11_deinterlace_prepare_fallback_pool (GstD3D11Deinterlace * self,
1024     GstCaps * in_caps, GstVideoInfo * in_info, GstCaps * out_caps,
1025     GstVideoInfo * out_info)
1026 {
1027   GstD3D11AllocationParams *d3d11_params;
1028
1029   /* Clearing potentially remaining resource here would be redundant.
1030    * Just to be safe enough */
1031   g_queue_clear_full (&self->past_frame_queue,
1032       (GDestroyNotify) gst_buffer_unref);
1033   g_queue_clear_full (&self->future_frame_queue,
1034       (GDestroyNotify) gst_buffer_unref);
1035
1036   if (self->fallback_in_pool) {
1037     gst_buffer_pool_set_active (self->fallback_in_pool, FALSE);
1038     gst_object_unref (self->fallback_in_pool);
1039     self->fallback_in_pool = NULL;
1040   }
1041
1042   if (self->fallback_out_pool) {
1043     gst_buffer_pool_set_active (self->fallback_out_pool, FALSE);
1044     gst_object_unref (self->fallback_out_pool);
1045     self->fallback_out_pool = NULL;
1046   }
1047
1048   /* Empty bind flag is allowed for video processor input */
1049   d3d11_params = gst_d3d11_allocation_params_new (self->device, in_info,
1050       GST_D3D11_ALLOCATION_FLAG_DEFAULT, 0, 0);
1051   self->fallback_in_pool = gst_d3d11_buffer_pool_new_with_options (self->device,
1052       in_caps, d3d11_params, 0, 0);
1053   gst_d3d11_allocation_params_free (d3d11_params);
1054
1055   if (!self->fallback_in_pool) {
1056     GST_ERROR_OBJECT (self, "Failed to create input fallback buffer pool");
1057     return FALSE;
1058   }
1059
1060   /* For processor output, render target bind flag is required */
1061   d3d11_params = gst_d3d11_allocation_params_new (self->device, out_info,
1062       GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_RENDER_TARGET, 0);
1063   self->fallback_out_pool =
1064       gst_d3d11_buffer_pool_new_with_options (self->device,
1065       out_caps, d3d11_params, 0, 0);
1066   gst_d3d11_allocation_params_free (d3d11_params);
1067
1068   if (!self->fallback_out_pool) {
1069     GST_ERROR_OBJECT (self, "Failed to create output fallback buffer pool");
1070     gst_clear_object (&self->fallback_out_pool);
1071     return FALSE;
1072   }
1073
1074   return TRUE;
1075 }
1076
1077 static gboolean
1078 gst_d3d11_deinterlace_set_caps (GstBaseTransform * trans,
1079     GstCaps * incaps, GstCaps * outcaps)
1080 {
1081   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1082   GstVideoInfo in_info, out_info;
1083   /* *INDENT-OFF* */
1084   ComPtr<ID3D11VideoProcessorEnumerator> video_enum;
1085   ComPtr<ID3D11VideoProcessor> video_proc;
1086   /* *INDENT-ON* */
1087   D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
1088   D3D11_VIDEO_PROCESSOR_CAPS proc_caps;
1089   D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rate_conv_caps;
1090   D3D11_VIDEO_PROCESSOR_OUTPUT_RATE output_rate =
1091       D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL;
1092   HRESULT hr;
1093   RECT rect;
1094   guint i;
1095
1096   if (gst_base_transform_is_passthrough (trans))
1097     return TRUE;
1098
1099   if (!gst_video_info_from_caps (&in_info, incaps)) {
1100     GST_ERROR_OBJECT (self, "Invalid input caps %" GST_PTR_FORMAT, incaps);
1101     return FALSE;
1102   }
1103
1104   if (!gst_video_info_from_caps (&out_info, outcaps)) {
1105     GST_ERROR_OBJECT (self, "Invalid output caps %" GST_PTR_FORMAT, outcaps);
1106     return FALSE;
1107   }
1108
1109   self->in_info = in_info;
1110   self->out_info = out_info;
1111
1112   /* Calculate expected buffer duration. We might need to reference this value
1113    * when buffer duration is unknown */
1114   if (GST_VIDEO_INFO_FPS_N (&in_info) > 0 &&
1115       GST_VIDEO_INFO_FPS_D (&in_info) > 0) {
1116     self->default_buffer_duration =
1117         gst_util_uint64_scale_int (GST_SECOND, GST_VIDEO_INFO_FPS_D (&in_info),
1118         GST_VIDEO_INFO_FPS_N (&in_info));
1119   } else {
1120     /* Assume 25 fps. We need this for reporting latency at least  */
1121     self->default_buffer_duration =
1122         gst_util_uint64_scale_int (GST_SECOND, 1, 25);
1123   }
1124
1125   gst_d3d11_deinterlace_reset (self);
1126
1127   /* Nothing to do */
1128   if (!GST_VIDEO_INFO_IS_INTERLACED (&in_info)) {
1129     gst_base_transform_set_passthrough (trans, TRUE);
1130
1131     return TRUE;
1132   }
1133
1134   /* TFF or BFF is not important here, this is just for enumerating
1135    * available deinterlace devices */
1136   memset (&desc, 0, sizeof (D3D11_VIDEO_PROCESSOR_CONTENT_DESC));
1137
1138   desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
1139   if (GST_VIDEO_INFO_FIELD_ORDER (&in_info) ==
1140       GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST)
1141     desc.InputFrameFormat =
1142         D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
1143   desc.InputWidth = GST_VIDEO_INFO_WIDTH (&in_info);
1144   desc.InputHeight = GST_VIDEO_INFO_HEIGHT (&in_info);
1145   desc.OutputWidth = GST_VIDEO_INFO_WIDTH (&out_info);
1146   desc.OutputHeight = GST_VIDEO_INFO_HEIGHT (&out_info);
1147   desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
1148
1149   hr = self->video_device->CreateVideoProcessorEnumerator (&desc, &video_enum);
1150   if (!gst_d3d11_result (hr, self->device)) {
1151     GST_ERROR_OBJECT (self, "Couldn't create VideoProcessorEnumerator");
1152     return FALSE;
1153   }
1154
1155   hr = video_enum->GetVideoProcessorCaps (&proc_caps);
1156   if (!gst_d3d11_result (hr, self->device)) {
1157     GST_ERROR_OBJECT (self, "Couldn't query processor caps");
1158     return FALSE;
1159   }
1160
1161   /* Shouldn't happen, we checked this already during plugin_init */
1162   if (proc_caps.RateConversionCapsCount == 0) {
1163     GST_ERROR_OBJECT (self, "Deinterlacing is not supported");
1164     return FALSE;
1165   }
1166
1167   for (i = 0; i < proc_caps.RateConversionCapsCount; i++) {
1168     hr = video_enum->GetVideoProcessorRateConversionCaps (i, &rate_conv_caps);
1169     if (FAILED (hr))
1170       continue;
1171
1172     if ((rate_conv_caps.ProcessorCaps & self->method) == self->method)
1173       break;
1174   }
1175
1176   if (i >= proc_caps.RateConversionCapsCount) {
1177     GST_ERROR_OBJECT (self, "Deinterlacing method 0x%x is not supported",
1178         self->method);
1179     return FALSE;
1180   }
1181
1182   hr = self->video_device->CreateVideoProcessor (video_enum.Get (),
1183       i, &video_proc);
1184   if (!gst_d3d11_result (hr, self->device)) {
1185     GST_ERROR_OBJECT (self, "Couldn't create processor");
1186     return FALSE;
1187   }
1188
1189   if (!gst_d3d11_deinterlace_prepare_fallback_pool (self, incaps, &in_info,
1190           outcaps, &out_info)) {
1191     GST_ERROR_OBJECT (self, "Couldn't prepare fallback buffer pool");
1192     return FALSE;
1193   }
1194
1195   self->video_enum = video_enum.Detach ();
1196   self->video_proc = video_proc.Detach ();
1197
1198   rect.left = 0;
1199   rect.top = 0;
1200   rect.right = GST_VIDEO_INFO_WIDTH (&self->in_info);
1201   rect.bottom = GST_VIDEO_INFO_HEIGHT (&self->in_info);
1202
1203   /* Blending seems to be considered as half rate. See also
1204    * https://docs.microsoft.com/en-us/windows/win32/api/d3d12video/ns-d3d12video-d3d12_video_process_input_stream_rate */
1205   if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND)
1206     output_rate = D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_HALF;
1207
1208   GstD3D11DeviceLockGuard lk (self->device);
1209   self->video_context->VideoProcessorSetStreamSourceRect (self->video_proc,
1210       0, TRUE, &rect);
1211   self->video_context->VideoProcessorSetStreamDestRect (self->video_proc,
1212       0, TRUE, &rect);
1213   self->video_context->VideoProcessorSetOutputTargetRect (self->video_proc,
1214       TRUE, &rect);
1215   self->video_context->
1216       VideoProcessorSetStreamAutoProcessingMode (self->video_proc, 0, FALSE);
1217   self->video_context->VideoProcessorSetStreamOutputRate (self->video_proc, 0,
1218       output_rate, TRUE, NULL);
1219
1220   return TRUE;
1221 }
1222
1223 static ID3D11VideoProcessorInputView *
1224 gst_d3d11_deinterace_get_piv_from_buffer (GstD3D11Deinterlace * self,
1225     GstBuffer * buffer)
1226 {
1227   GstMemory *mem;
1228   GstD3D11Memory *dmem;
1229   ID3D11VideoProcessorInputView *piv;
1230
1231   if (gst_buffer_n_memory (buffer) != 1) {
1232     GST_WARNING_OBJECT (self, "Input buffer has more than one memory");
1233     return NULL;
1234   }
1235
1236   mem = gst_buffer_peek_memory (buffer, 0);
1237   if (!gst_is_d3d11_memory (mem)) {
1238     GST_WARNING_OBJECT (self, "Input buffer is holding non-D3D11 memory");
1239     return NULL;
1240   }
1241
1242   dmem = (GstD3D11Memory *) mem;
1243   if (dmem->device != self->device) {
1244     GST_WARNING_OBJECT (self,
1245         "Input D3D11 memory was allocated by other device");
1246     return NULL;
1247   }
1248
1249   piv = gst_d3d11_memory_get_processor_input_view (dmem,
1250       self->video_device, self->video_enum);
1251   if (!piv) {
1252     GST_WARNING_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
1253     return NULL;
1254   }
1255
1256   return piv;
1257 }
1258
1259 static GstBuffer *
1260 gst_d3d11_deinterlace_ensure_input_buffer (GstD3D11Deinterlace * self,
1261     GstBuffer * input)
1262 {
1263   GstD3D11Memory *dmem;
1264   ID3D11VideoProcessorInputView *piv;
1265   GstBuffer *new_buf = NULL;
1266
1267   if (!input)
1268     return NULL;
1269
1270   piv = gst_d3d11_deinterace_get_piv_from_buffer (self, input);
1271   if (piv)
1272     return input;
1273
1274   if (!self->fallback_in_pool ||
1275       !gst_buffer_pool_set_active (self->fallback_in_pool, TRUE) ||
1276       gst_buffer_pool_acquire_buffer (self->fallback_in_pool, &new_buf,
1277           NULL) != GST_FLOW_OK) {
1278     GST_ERROR_OBJECT (self, "Fallback input buffer is unavailable");
1279     gst_buffer_unref (input);
1280
1281     return NULL;
1282   }
1283
1284   if (!gst_d3d11_buffer_copy_into (new_buf, input, &self->in_info)) {
1285     GST_ERROR_OBJECT (self, "Couldn't copy input buffer to fallback buffer");
1286     gst_buffer_unref (new_buf);
1287     gst_buffer_unref (input);
1288
1289     return NULL;
1290   }
1291
1292   dmem = (GstD3D11Memory *) gst_buffer_peek_memory (new_buf, 0);
1293   piv = gst_d3d11_memory_get_processor_input_view (dmem,
1294       self->video_device, self->video_enum);
1295   if (!piv) {
1296     GST_ERROR_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
1297     gst_buffer_unref (new_buf);
1298     gst_buffer_unref (input);
1299
1300     return NULL;
1301   }
1302
1303   /* copy metadata, default implementation of baseclass will copy everything
1304    * what we need */
1305   GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
1306       (GST_BASE_TRANSFORM_CAST (self), input, new_buf);
1307
1308   gst_buffer_unref (input);
1309
1310   return new_buf;
1311 }
1312
1313 static GstFlowReturn
1314 gst_d3d11_deinterlace_submit_future_frame (GstD3D11Deinterlace * self,
1315     GstBuffer * buffer)
1316 {
1317   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (self);
1318   guint len;
1319
1320   /* push tail and pop head, so that head frame can be the nearest frame
1321    * of current frame */
1322   if (buffer)
1323     g_queue_push_tail (&self->future_frame_queue, buffer);
1324
1325   len = g_queue_get_length (&self->future_frame_queue);
1326
1327   g_assert (len <= self->max_future_frames + 1);
1328
1329   if (self->to_process) {
1330     GST_WARNING_OBJECT (self, "Found uncleared processing buffer");
1331     gst_clear_buffer (&self->to_process);
1332   }
1333
1334   if (len > self->max_future_frames ||
1335       /* NULL means drain */
1336       (buffer == NULL && len > 0)) {
1337     GstClockTime cur_timestmap = GST_CLOCK_TIME_NONE;
1338     GstClockTime duration = GST_CLOCK_TIME_NONE;
1339     GstBuffer *next_buf;
1340
1341     self->to_process =
1342         (GstBuffer *) g_queue_pop_head (&self->future_frame_queue);
1343
1344     /* For non-blend methods, we will produce two frames from a single
1345      * interlaced frame. So, sufficiently correct buffer duration is required
1346      * to set timestamp for the second output frame */
1347     if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND) {
1348       if (GST_BUFFER_PTS_IS_VALID (self->to_process)) {
1349         cur_timestmap = GST_BUFFER_PTS (self->to_process);
1350       } else {
1351         cur_timestmap = GST_BUFFER_DTS (self->to_process);
1352       }
1353
1354       /* Ensure buffer duration */
1355       next_buf = (GstBuffer *) g_queue_peek_head (&self->future_frame_queue);
1356       if (next_buf && GST_CLOCK_STIME_IS_VALID (cur_timestmap)) {
1357         GstClockTime next_timestamp;
1358
1359         if (GST_BUFFER_PTS_IS_VALID (next_buf)) {
1360           next_timestamp = GST_BUFFER_PTS (next_buf);
1361         } else {
1362           next_timestamp = GST_BUFFER_DTS (next_buf);
1363         }
1364
1365         if (GST_CLOCK_STIME_IS_VALID (next_timestamp)) {
1366           if (trans->segment.rate >= 0.0 && next_timestamp > cur_timestmap) {
1367             duration = next_timestamp - cur_timestmap;
1368           } else if (trans->segment.rate < 0.0
1369               && next_timestamp < cur_timestmap) {
1370             duration = cur_timestmap - next_timestamp;
1371           }
1372         }
1373       }
1374
1375       /* Make sure that we can update buffer duration safely */
1376       self->to_process = gst_buffer_make_writable (self->to_process);
1377       if (GST_CLOCK_TIME_IS_VALID (duration)) {
1378         GST_BUFFER_DURATION (self->to_process) = duration;
1379       } else {
1380         GST_BUFFER_DURATION (self->to_process) = self->default_buffer_duration;
1381       }
1382
1383       /* Bonus points, DTS doesn't make sense for raw video frame */
1384       GST_BUFFER_PTS (self->to_process) = cur_timestmap;
1385       GST_BUFFER_DTS (self->to_process) = GST_CLOCK_TIME_NONE;
1386
1387       /* And mark the number of output frames for this input frame */
1388       self->num_output_per_input = 2;
1389     } else {
1390       self->num_output_per_input = 1;
1391     }
1392
1393     self->first_output = TRUE;
1394   }
1395
1396   return GST_FLOW_OK;
1397 }
1398
1399 static GstFlowReturn
1400 gst_d3d11_deinterlace_submit_input_buffer (GstBaseTransform * trans,
1401     gboolean is_discont, GstBuffer * input)
1402 {
1403   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1404   GstFlowReturn ret;
1405   GstBuffer *buf;
1406
1407   /* Let baseclass handle QoS first */
1408   ret = GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans,
1409       is_discont, input);
1410   if (ret != GST_FLOW_OK)
1411     return ret;
1412
1413   if (gst_base_transform_is_passthrough (trans))
1414     return ret;
1415
1416   /* at this moment, baseclass must hold queued_buf */
1417   g_assert (trans->queued_buf != NULL);
1418
1419   /* Check if we can use this buffer directly. If not, copy this into
1420    * our fallback buffer */
1421   buf = trans->queued_buf;
1422   trans->queued_buf = NULL;
1423
1424   buf = gst_d3d11_deinterlace_ensure_input_buffer (self, buf);
1425   if (!buf) {
1426     GST_ERROR_OBJECT (self, "Invalid input buffer");
1427     return GST_FLOW_ERROR;
1428   }
1429
1430   return gst_d3d11_deinterlace_submit_future_frame (self, buf);
1431 }
1432
1433 static ID3D11VideoProcessorOutputView *
1434 gst_d3d11_deinterace_get_pov_from_buffer (GstD3D11Deinterlace * self,
1435     GstBuffer * buffer)
1436 {
1437   GstMemory *mem;
1438   GstD3D11Memory *dmem;
1439   ID3D11VideoProcessorOutputView *pov;
1440
1441   if (gst_buffer_n_memory (buffer) != 1) {
1442     GST_WARNING_OBJECT (self, "Output buffer has more than one memory");
1443     return NULL;
1444   }
1445
1446   mem = gst_buffer_peek_memory (buffer, 0);
1447   if (!gst_is_d3d11_memory (mem)) {
1448     GST_WARNING_OBJECT (self, "Output buffer is holding non-D3D11 memory");
1449     return NULL;
1450   }
1451
1452   dmem = (GstD3D11Memory *) mem;
1453   if (dmem->device != self->device) {
1454     GST_WARNING_OBJECT (self,
1455         "Output D3D11 memory was allocated by other device");
1456     return NULL;
1457   }
1458
1459   pov = gst_d3d11_memory_get_processor_output_view (dmem,
1460       self->video_device, self->video_enum);
1461   if (!pov) {
1462     GST_WARNING_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
1463     return NULL;
1464   }
1465
1466   return pov;
1467 }
1468
1469 static GstBuffer *
1470 gst_d3d11_deinterlace_ensure_output_buffer (GstD3D11Deinterlace * self,
1471     GstBuffer * output)
1472 {
1473   GstD3D11Memory *dmem;
1474   ID3D11VideoProcessorOutputView *pov;
1475   GstBuffer *new_buf = NULL;
1476
1477   pov = gst_d3d11_deinterace_get_pov_from_buffer (self, output);
1478   if (pov)
1479     return output;
1480
1481   if (!self->fallback_out_pool ||
1482       !gst_buffer_pool_set_active (self->fallback_out_pool, TRUE) ||
1483       gst_buffer_pool_acquire_buffer (self->fallback_out_pool, &new_buf,
1484           NULL) != GST_FLOW_OK) {
1485     GST_ERROR_OBJECT (self, "Fallback output buffer is unavailable");
1486     gst_buffer_unref (output);
1487
1488     return NULL;
1489   }
1490
1491   dmem = (GstD3D11Memory *) gst_buffer_peek_memory (new_buf, 0);
1492   pov = gst_d3d11_memory_get_processor_output_view (dmem,
1493       self->video_device, self->video_enum);
1494   if (!pov) {
1495     GST_ERROR_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
1496     gst_buffer_unref (new_buf);
1497     gst_buffer_unref (output);
1498
1499     return NULL;
1500   }
1501
1502   /* copy metadata, default implementation of baseclass will copy everything
1503    * what we need */
1504   GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
1505       (GST_BASE_TRANSFORM_CAST (self), output, new_buf);
1506
1507   gst_buffer_unref (output);
1508
1509   return new_buf;
1510 }
1511
1512 static GstFlowReturn
1513 gst_d3d11_deinterlace_submit_past_frame (GstD3D11Deinterlace * self,
1514     GstBuffer * buffer)
1515 {
1516   /* push head and pop tail, so that head frame can be the nearest frame
1517    * of current frame */
1518   g_queue_push_head (&self->past_frame_queue, buffer);
1519   while (g_queue_get_length (&self->past_frame_queue) > self->max_past_frames) {
1520     GstBuffer *to_drop =
1521         (GstBuffer *) g_queue_pop_tail (&self->past_frame_queue);
1522
1523     if (to_drop)
1524       gst_buffer_unref (to_drop);
1525   }
1526
1527   return GST_FLOW_OK;
1528 }
1529
1530 static GstFlowReturn
1531 gst_d3d11_deinterlace_generate_output (GstBaseTransform * trans,
1532     GstBuffer ** outbuf)
1533 {
1534   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1535   GstFlowReturn ret = GST_FLOW_OK;
1536   GstBuffer *inbuf;
1537   GstBuffer *buf = NULL;
1538
1539   if (gst_base_transform_is_passthrough (trans)) {
1540     return GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
1541         outbuf);
1542   }
1543
1544   *outbuf = NULL;
1545   inbuf = self->to_process;
1546   if (inbuf == NULL)
1547     return GST_FLOW_OK;
1548
1549   ret =
1550       GST_BASE_TRANSFORM_CLASS (parent_class)->prepare_output_buffer (trans,
1551       inbuf, &buf);
1552
1553   if (ret != GST_FLOW_OK || !buf) {
1554     GST_WARNING_OBJECT (trans, "could not get buffer from pool: %s",
1555         gst_flow_get_name (ret));
1556
1557     return ret;
1558   }
1559
1560   g_assert (inbuf != buf);
1561
1562   buf = gst_d3d11_deinterlace_ensure_output_buffer (self, buf);
1563   if (!buf) {
1564     GST_ERROR_OBJECT (self, "Failed to allocate output buffer to process");
1565
1566     return GST_FLOW_ERROR;
1567   }
1568
1569   ret = gst_d3d11_deinterlace_transform (trans, inbuf, buf);
1570   if (ret != GST_FLOW_OK) {
1571     gst_buffer_unref (buf);
1572     return ret;
1573   }
1574
1575   g_assert (self->num_output_per_input == 1 || self->num_output_per_input == 2);
1576
1577   /* Update timestamp and buffer duration.
1578    * Here, PTS and duration of inbuf must be valid,
1579    * unless there's programing error, since we updated timestamp and duration
1580    * already around submit_input_buffer()  */
1581   if (self->num_output_per_input == 2) {
1582     if (!GST_BUFFER_DURATION_IS_VALID (inbuf)) {
1583       GST_LOG_OBJECT (self, "Input buffer duration is unknown");
1584     } else if (!GST_BUFFER_PTS_IS_VALID (inbuf)) {
1585       GST_LOG_OBJECT (self, "Input buffer timestamp is unknown");
1586     } else {
1587       GstClockTime duration = GST_BUFFER_DURATION (inbuf) / 2;
1588       gboolean second_field = FALSE;
1589
1590       if (self->first_output) {
1591         /* For reverse playback, first output is the second field */
1592         if (trans->segment.rate < 0)
1593           second_field = TRUE;
1594         else
1595           second_field = FALSE;
1596       } else {
1597         if (trans->segment.rate < 0)
1598           second_field = FALSE;
1599         else
1600           second_field = TRUE;
1601       }
1602
1603       GST_BUFFER_DURATION (buf) = duration;
1604       if (second_field) {
1605         GST_BUFFER_PTS (buf) = GST_BUFFER_PTS (buf) + duration;
1606       }
1607     }
1608   }
1609
1610   *outbuf = buf;
1611   self->first_output = FALSE;
1612   self->num_transformed++;
1613   /* https://docs.microsoft.com/en-us/windows/win32/api/d3d12video/ns-d3d12video-d3d12_video_process_input_stream_rate */
1614   if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND) {
1615     self->input_index += 2;
1616   } else {
1617     self->input_index++;
1618   }
1619
1620   if (self->num_output_per_input <= self->num_transformed) {
1621     /* Move processed frame to past_frame queue */
1622     gst_d3d11_deinterlace_submit_past_frame (self, self->to_process);
1623     self->to_process = NULL;
1624   }
1625
1626   return ret;
1627 }
1628
1629 static GstFlowReturn
1630 gst_d3d11_deinterlace_transform (GstBaseTransform * trans, GstBuffer * inbuf,
1631     GstBuffer * outbuf)
1632 {
1633   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1634   ID3D11VideoProcessorInputView *piv;
1635   ID3D11VideoProcessorOutputView *pov;
1636   D3D11_VIDEO_FRAME_FORMAT frame_foramt = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
1637   D3D11_VIDEO_PROCESSOR_STREAM proc_stream = { 0, };
1638   ID3D11VideoProcessorInputView *future_surfaces[MAX_NUM_REFERENCES] =
1639       { NULL, };
1640   ID3D11VideoProcessorInputView *past_surfaces[MAX_NUM_REFERENCES] = { NULL, };
1641   guint future_frames = 0;
1642   guint past_frames = 0;
1643   HRESULT hr;
1644   guint i;
1645
1646   /* Input/output buffer must be holding valid D3D11 memory here,
1647    * as we checked it already in submit_input_buffer() and generate_output() */
1648   piv = gst_d3d11_deinterace_get_piv_from_buffer (self, inbuf);
1649   if (!piv) {
1650     GST_ERROR_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
1651     return GST_FLOW_ERROR;
1652   }
1653
1654   pov = gst_d3d11_deinterace_get_pov_from_buffer (self, outbuf);
1655   if (!pov) {
1656     GST_ERROR_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
1657     return GST_FLOW_ERROR;
1658   }
1659
1660   /* Check field order */
1661   if (GST_VIDEO_INFO_INTERLACE_MODE (&self->in_info) ==
1662       GST_VIDEO_INTERLACE_MODE_MIXED ||
1663       (GST_VIDEO_INFO_INTERLACE_MODE (&self->in_info) ==
1664           GST_VIDEO_INTERLACE_MODE_INTERLEAVED &&
1665           GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
1666           GST_VIDEO_FIELD_ORDER_UNKNOWN)) {
1667     if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
1668       frame_foramt = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
1669     } else if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF)) {
1670       frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
1671     } else {
1672       frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
1673     }
1674   } else if (GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
1675       GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
1676     frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
1677   } else if (GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
1678       GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST) {
1679     frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
1680   }
1681
1682   if (frame_foramt == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE) {
1683     /* Progressive stream will produce only one frame per frame */
1684     self->num_output_per_input = 1;
1685   } else if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND &&
1686       self->method != GST_D3D11_DEINTERLACE_METHOD_BOB) {
1687     /* Fill reference frames */
1688     for (i = 0; i < g_queue_get_length (&self->future_frame_queue) &&
1689         i < G_N_ELEMENTS (future_surfaces); i++) {
1690       GstBuffer *future_buf;
1691       ID3D11VideoProcessorInputView *future_piv;
1692
1693       future_buf =
1694           (GstBuffer *) g_queue_peek_nth (&self->future_frame_queue, i);
1695       future_piv = gst_d3d11_deinterace_get_piv_from_buffer (self, future_buf);
1696       if (!future_piv) {
1697         GST_WARNING_OBJECT (self,
1698             "Couldn't get ID3D11VideoProcessorInputView from future "
1699             "reference %d", i);
1700         break;
1701       }
1702
1703       future_surfaces[i] = future_piv;
1704       future_frames++;
1705     }
1706
1707     for (i = 0; i < g_queue_get_length (&self->past_frame_queue) &&
1708         i < G_N_ELEMENTS (past_surfaces); i++) {
1709       GstBuffer *past_buf;
1710       ID3D11VideoProcessorInputView *past_piv;
1711
1712       past_buf = (GstBuffer *) g_queue_peek_nth (&self->past_frame_queue, i);
1713       past_piv = gst_d3d11_deinterace_get_piv_from_buffer (self, past_buf);
1714       if (!past_piv) {
1715         GST_WARNING_OBJECT (self,
1716             "Couldn't get ID3D11VideoProcessorInputView from past "
1717             "reference %d", i);
1718         break;
1719       }
1720
1721       past_surfaces[i] = past_piv;
1722       past_frames++;
1723     }
1724   }
1725
1726   proc_stream.Enable = TRUE;
1727   proc_stream.pInputSurface = piv;
1728   proc_stream.InputFrameOrField = self->input_index;
1729   /* FIXME: This is wrong for inverse telechin case */
1730   /* OutputIndex == 0 for the first field, and 1 for the second field */
1731   if (self->num_output_per_input == 2) {
1732     if (trans->segment.rate < 0.0) {
1733       /* Process the second frame first in case of reverse playback */
1734       proc_stream.OutputIndex = self->first_output ? 1 : 0;
1735     } else {
1736       proc_stream.OutputIndex = self->first_output ? 0 : 1;
1737     }
1738   } else {
1739     proc_stream.OutputIndex = 0;
1740   }
1741
1742   if (future_frames) {
1743     proc_stream.FutureFrames = future_frames;
1744     proc_stream.ppFutureSurfaces = future_surfaces;
1745   }
1746
1747   if (past_frames) {
1748     proc_stream.PastFrames = past_frames;
1749     proc_stream.ppPastSurfaces = past_surfaces;
1750   }
1751
1752   GstD3D11DeviceLockGuard lk (self->device);
1753   self->video_context->VideoProcessorSetStreamFrameFormat (self->video_proc, 0,
1754       frame_foramt);
1755
1756   hr = self->video_context->VideoProcessorBlt (self->video_proc, pov, 0,
1757       1, &proc_stream);
1758
1759   if (!gst_d3d11_result (hr, self->device)) {
1760     GST_ERROR_OBJECT (self, "Failed to perform deinterlacing");
1761     return GST_FLOW_ERROR;
1762   }
1763
1764   return GST_FLOW_OK;
1765 }
1766
1767 static gboolean
1768 gst_d3d11_deinterlace_sink_event (GstBaseTransform * trans, GstEvent * event)
1769 {
1770   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1771
1772   switch (GST_EVENT_TYPE (event)) {
1773     case GST_EVENT_STREAM_START:
1774       /* stream-start means discont stream from previous one. Drain pending
1775        * frame if any */
1776       GST_DEBUG_OBJECT (self, "Have stream-start, drain frames if any");
1777       gst_d3d11_deinterlace_drain (self);
1778       break;
1779     case GST_EVENT_CAPS:{
1780       GstPad *sinkpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
1781       GstCaps *prev_caps;
1782
1783       prev_caps = gst_pad_get_current_caps (sinkpad);
1784       if (prev_caps) {
1785         GstCaps *caps;
1786         gst_event_parse_caps (event, &caps);
1787         /* If caps is updated, drain pending frames */
1788         if (!gst_caps_is_equal (prev_caps, caps)) {
1789           GST_DEBUG_OBJECT (self, "Caps updated from %" GST_PTR_FORMAT " to %"
1790               GST_PTR_FORMAT, prev_caps, caps);
1791           gst_d3d11_deinterlace_drain (self);
1792         }
1793
1794         gst_caps_unref (prev_caps);
1795       }
1796       break;
1797     }
1798     case GST_EVENT_SEGMENT:
1799       /* new segment would mean that temporal discontinuity */
1800     case GST_EVENT_SEGMENT_DONE:
1801     case GST_EVENT_EOS:
1802       GST_DEBUG_OBJECT (self, "Have event %s, drain frames if any",
1803           GST_EVENT_TYPE_NAME (event));
1804       gst_d3d11_deinterlace_drain (self);
1805       break;
1806     case GST_EVENT_FLUSH_STOP:
1807       EnterCriticalSection (&self->lock);
1808       gst_d3d11_deinterlace_reset_history (self);
1809       LeaveCriticalSection (&self->lock);
1810       break;
1811     default:
1812       break;
1813   }
1814
1815   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
1816 }
1817
1818 static void
1819 gst_d3d11_deinterlace_before_transform (GstBaseTransform * trans,
1820     GstBuffer * buffer)
1821 {
1822   GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
1823   GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
1824   GstD3D11Memory *dmem;
1825   GstMemory *mem;
1826   GstCaps *in_caps = NULL;
1827   GstCaps *out_caps = NULL;
1828   guint adapter = 0;
1829
1830   mem = gst_buffer_peek_memory (buffer, 0);
1831   if (!gst_is_d3d11_memory (mem)) {
1832     GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL), ("Invalid memory"));
1833     return;
1834   }
1835
1836   dmem = GST_D3D11_MEMORY_CAST (mem);
1837   /* Same device, nothing to do */
1838   if (dmem->device == self->device)
1839     return;
1840
1841   g_object_get (dmem->device, "adapter", &adapter, NULL);
1842   /* We have per-GPU deinterlace elements because of different capability
1843    * per GPU. so, cannot accept other GPU at the moment */
1844   if (adapter != klass->adapter)
1845     return;
1846
1847   GST_INFO_OBJECT (self, "Updating device %" GST_PTR_FORMAT " -> %"
1848       GST_PTR_FORMAT, self->device, dmem->device);
1849
1850   /* Drain buffers before updating device */
1851   gst_d3d11_deinterlace_drain (self);
1852
1853   gst_object_unref (self->device);
1854   self->device = (GstD3D11Device *) gst_object_ref (dmem->device);
1855
1856   in_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM_SINK_PAD (trans));
1857   if (!in_caps) {
1858     GST_WARNING_OBJECT (self, "sinkpad has null caps");
1859     goto out;
1860   }
1861
1862   out_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM_SRC_PAD (trans));
1863   if (!out_caps) {
1864     GST_WARNING_OBJECT (self, "Has no configured output caps");
1865     goto out;
1866   }
1867
1868   gst_d3d11_deinterlace_set_caps (trans, in_caps, out_caps);
1869
1870   /* Mark reconfigure so that we can update pool */
1871   gst_base_transform_reconfigure_src (trans);
1872
1873 out:
1874   gst_clear_caps (&in_caps);
1875   gst_clear_caps (&out_caps);
1876
1877   return;
1878 }
1879
1880 /* FIXME: might be job of basetransform */
1881 static GstFlowReturn
1882 gst_d3d11_deinterlace_drain (GstD3D11Deinterlace * self)
1883 {
1884   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (self);
1885   GstFlowReturn ret = GST_FLOW_OK;
1886   GstBuffer *outbuf = NULL;
1887
1888   EnterCriticalSection (&self->lock);
1889
1890   if (gst_base_transform_is_passthrough (trans)) {
1891     /* If we were passthrough, nothing to do */
1892     goto done;
1893   } else if (!g_queue_get_length (&self->future_frame_queue)) {
1894     /* No pending data, nothing to do */
1895     goto done;
1896   }
1897
1898   while (g_queue_get_length (&self->future_frame_queue)) {
1899     gst_d3d11_deinterlace_submit_future_frame (self, NULL);
1900     if (!self->to_process)
1901       break;
1902
1903     do {
1904       outbuf = NULL;
1905
1906       ret = gst_d3d11_deinterlace_generate_output (trans, &outbuf);
1907       if (outbuf != NULL) {
1908         /* Release lock during push buffer */
1909         LeaveCriticalSection (&self->lock);
1910         ret = gst_pad_push (trans->srcpad, outbuf);
1911         EnterCriticalSection (&self->lock);
1912       }
1913     } while (ret == GST_FLOW_OK && outbuf != NULL);
1914   }
1915
1916 done:
1917   gst_d3d11_deinterlace_reset_history (self);
1918   LeaveCriticalSection (&self->lock);
1919
1920   return ret;
1921 }
1922
1923 /**
1924  * SECTION:element-d3d11deinterlace
1925  * @title: d3d11deinterlace
1926  * @short_description: A Direct3D11 based deinterlace element
1927  *
1928  * Deinterlacing interlaced video frames to progressive video frames by using
1929  * ID3D11VideoProcessor API.
1930  *
1931  * ## Example launch line
1932  * ```
1933  * gst-launch-1.0 filesrc location=/path/to/h264/file ! parsebin ! d3d11h264dec ! d3d11deinterlace ! d3d11videosink
1934  * ```
1935  *
1936  * Since: 1.20
1937  *
1938  */
1939
1940 /* GstD3D11DeinterlaceBin */
1941 enum
1942 {
1943   PROP_BIN_0,
1944   /* basetransform */
1945   PROP_BIN_QOS,
1946   /* deinterlace */
1947   PROP_BIN_ADAPTER,
1948   PROP_BIN_DEVICE_ID,
1949   PROP_BIN_VENDOR_ID,
1950   PROP_BIN_METHOD,
1951   PROP_BIN_SUPPORTED_METHODS,
1952 };
1953
1954 typedef struct _GstD3D11DeinterlaceBin
1955 {
1956   GstBin parent;
1957
1958   GstPad *sinkpad;
1959   GstPad *srcpad;
1960
1961   GstElement *deinterlace;
1962   GstElement *in_convert;
1963   GstElement *out_convert;
1964   GstElement *upload;
1965   GstElement *download;
1966 } GstD3D11DeinterlaceBin;
1967
1968 typedef struct _GstD3D11DeinterlaceBinClass
1969 {
1970   GstBinClass parent_class;
1971
1972   guint adapter;
1973   GType child_type;
1974 } GstD3D11DeinterlaceBinClass;
1975
1976 static GstElementClass *bin_parent_class = NULL;
1977 #define GST_D3D11_DEINTERLACE_BIN(object) ((GstD3D11DeinterlaceBin *) (object))
1978 #define GST_D3D11_DEINTERLACE_BIN_GET_CLASS(object) \
1979     (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object), \
1980     GstD3D11DeinterlaceBinClass))
1981
1982 #define GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE(format) \
1983     "video/x-raw, " \
1984     "format = (string) " format ", "  \
1985     "width = (int) [1, 8192], " \
1986     "height = (int) [1, 8192] "
1987
1988 #define GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES(features,format) \
1989     "video/x-raw(" features "), " \
1990     "format = (string) " format ", "  \
1991     "width = (int) [1, 8192], " \
1992     "height = (int) [1, 8192] "
1993
1994 static GstStaticPadTemplate bin_sink_template_caps =
1995     GST_STATIC_PAD_TEMPLATE ("sink",
1996     GST_PAD_SINK,
1997     GST_PAD_ALWAYS,
1998     GST_STATIC_CAPS (GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
1999         (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SINK_FORMATS) "; "
2000         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
2001         (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
2002             GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
2003             GST_D3D11_SINK_FORMATS) "; "
2004         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE (GST_D3D11_SINK_FORMATS) "; "
2005         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
2006         (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
2007             GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
2008             GST_D3D11_SINK_FORMATS)
2009     ));
2010
2011 static GstStaticPadTemplate bin_src_template_caps =
2012     GST_STATIC_PAD_TEMPLATE ("src",
2013     GST_PAD_SRC,
2014     GST_PAD_ALWAYS,
2015     GST_STATIC_CAPS (GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
2016         (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SRC_FORMATS) "; "
2017         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
2018         (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
2019             GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
2020             GST_D3D11_SRC_FORMATS) "; "
2021         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE (GST_D3D11_SRC_FORMATS) "; "
2022         GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE_WITH_FEATURES
2023         (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ","
2024             GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
2025             GST_D3D11_SRC_FORMATS)
2026     ));
2027
2028 static void gst_d3d11_deinterlace_bin_set_property (GObject * object,
2029     guint prop_id, const GValue * value, GParamSpec * pspec);
2030 static void gst_d3d11_deinterlace_bin_get_property (GObject * object,
2031     guint prop_id, GValue * value, GParamSpec * pspec);
2032
2033 static void
2034 gst_d3d11_deinterlace_bin_class_init (GstD3D11DeinterlaceBinClass * klass,
2035     gpointer data)
2036 {
2037   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2038   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
2039   GstD3D11DeinterlaceClassData *cdata = (GstD3D11DeinterlaceClassData *) data;
2040   gchar *long_name;
2041
2042   bin_parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
2043
2044   gobject_class->get_property = gst_d3d11_deinterlace_bin_get_property;
2045   gobject_class->set_property = gst_d3d11_deinterlace_bin_set_property;
2046
2047   /* basetransform */
2048   g_object_class_install_property (gobject_class, PROP_BIN_QOS,
2049       g_param_spec_boolean ("qos", "QoS", "Handle Quality-of-Service events",
2050           FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
2051
2052   /* deinterlace */
2053   g_object_class_install_property (gobject_class, PROP_BIN_ADAPTER,
2054       g_param_spec_uint ("adapter", "Adapter",
2055           "DXGI Adapter index for creating device",
2056           0, G_MAXUINT32, cdata->adapter,
2057           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2058   g_object_class_install_property (gobject_class, PROP_BIN_DEVICE_ID,
2059       g_param_spec_uint ("device-id", "Device Id",
2060           "DXGI Device ID", 0, G_MAXUINT32, 0,
2061           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2062   g_object_class_install_property (gobject_class, PROP_BIN_VENDOR_ID,
2063       g_param_spec_uint ("vendor-id", "Vendor Id",
2064           "DXGI Vendor ID", 0, G_MAXUINT32, 0,
2065           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2066   g_object_class_install_property (gobject_class, PROP_BIN_METHOD,
2067       g_param_spec_flags ("method", "Method",
2068           "Deinterlace Method. Use can set multiple methods as a flagset "
2069           "and element will select one of method automatically. "
2070           "If deinterlacing device failed to deinterlace with given mode, "
2071           "fallback might happen by the device",
2072           GST_TYPE_D3D11_DEINTERLACE_METHOD, cdata->device_caps.default_method,
2073           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
2074               GST_PARAM_MUTABLE_READY)));
2075   g_object_class_install_property (gobject_class, PROP_BIN_SUPPORTED_METHODS,
2076       g_param_spec_flags ("supported-methods", "Supported Methods",
2077           "Set of supported deinterlace methods by device",
2078           GST_TYPE_D3D11_DEINTERLACE_METHOD,
2079           cdata->device_caps.supported_methods,
2080           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2081
2082   long_name = g_strdup_printf ("Direct3D11 %s Deinterlacer Bin",
2083       cdata->description);
2084   gst_element_class_set_metadata (element_class, long_name,
2085       "Filter/Effect/Video/Deinterlace/Hardware",
2086       "A Direct3D11 based deinterlacer bin",
2087       "Seungha Yang <seungha@centricular.com>");
2088   g_free (long_name);
2089
2090   gst_element_class_add_static_pad_template (element_class,
2091       &bin_sink_template_caps);
2092   gst_element_class_add_static_pad_template (element_class,
2093       &bin_src_template_caps);
2094
2095   klass->adapter = cdata->adapter;
2096   klass->child_type = cdata->deinterlace_type;
2097
2098   gst_d3d11_deinterlace_class_data_unref (cdata);
2099 }
2100
2101 static void
2102 gst_d3d11_deinterlace_bin_init (GstD3D11DeinterlaceBin * self)
2103 {
2104   GstD3D11DeinterlaceBinClass *klass =
2105       GST_D3D11_DEINTERLACE_BIN_GET_CLASS (self);
2106   GstPad *pad;
2107
2108   self->deinterlace = (GstElement *) g_object_new (klass->child_type,
2109       "name", "deinterlace", NULL);
2110   self->in_convert = gst_element_factory_make ("d3d11colorconvert", NULL);
2111   self->out_convert = gst_element_factory_make ("d3d11colorconvert", NULL);
2112   self->upload = gst_element_factory_make ("d3d11upload", NULL);
2113   self->download = gst_element_factory_make ("d3d11download", NULL);
2114
2115   /* Specify DXGI adapter index to use */
2116   g_object_set (G_OBJECT (self->in_convert), "adapter", klass->adapter, NULL);
2117   g_object_set (G_OBJECT (self->out_convert), "adapter", klass->adapter, NULL);
2118   g_object_set (G_OBJECT (self->upload), "adapter", klass->adapter, NULL);
2119   g_object_set (G_OBJECT (self->download), "adapter", klass->adapter, NULL);
2120
2121   gst_bin_add_many (GST_BIN_CAST (self), self->upload, self->in_convert,
2122       self->deinterlace, self->out_convert, self->download, NULL);
2123   gst_element_link_many (self->upload, self->in_convert, self->deinterlace,
2124       self->out_convert, self->download, NULL);
2125
2126   pad = gst_element_get_static_pad (self->upload, "sink");
2127   self->sinkpad = gst_ghost_pad_new ("sink", pad);
2128   gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
2129   gst_object_unref (pad);
2130
2131   pad = gst_element_get_static_pad (self->download, "src");
2132   self->srcpad = gst_ghost_pad_new ("src", pad);
2133   gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
2134   gst_object_unref (pad);
2135 }
2136
2137 static void
2138 gst_d3d11_deinterlace_bin_set_property (GObject * object, guint prop_id,
2139     const GValue * value, GParamSpec * pspec)
2140 {
2141   GstD3D11DeinterlaceBin *self = GST_D3D11_DEINTERLACE_BIN (object);
2142
2143   g_object_set_property (G_OBJECT (self->deinterlace), pspec->name, value);
2144 }
2145
2146 static void
2147 gst_d3d11_deinterlace_bin_get_property (GObject * object, guint prop_id,
2148     GValue * value, GParamSpec * pspec)
2149 {
2150   GstD3D11DeinterlaceBin *self = GST_D3D11_DEINTERLACE_BIN (object);
2151
2152   g_object_get_property (G_OBJECT (self->deinterlace), pspec->name, value);
2153 }
2154
2155 void
2156 gst_d3d11_deinterlace_register (GstPlugin * plugin, GstD3D11Device * device,
2157     guint rank)
2158 {
2159   GType type;
2160   GType bin_type;
2161   gchar *type_name;
2162   gchar *feature_name;
2163   guint index = 0;
2164   GTypeInfo type_info = {
2165     sizeof (GstD3D11DeinterlaceClass),
2166     NULL,
2167     NULL,
2168     (GClassInitFunc) gst_d3d11_deinterlace_class_init,
2169     NULL,
2170     NULL,
2171     sizeof (GstD3D11Deinterlace),
2172     0,
2173     (GInstanceInitFunc) gst_d3d11_deinterlace_init,
2174   };
2175   GTypeInfo bin_type_info = {
2176     sizeof (GstD3D11DeinterlaceBinClass),
2177     NULL,
2178     NULL,
2179     (GClassInitFunc) gst_d3d11_deinterlace_bin_class_init,
2180     NULL,
2181     NULL,
2182     sizeof (GstD3D11DeinterlaceBin),
2183     0,
2184     (GInstanceInitFunc) gst_d3d11_deinterlace_bin_init,
2185   };
2186   GstCaps *sink_caps = NULL;
2187   GstCaps *src_caps = NULL;
2188   GstCaps *caps = NULL;
2189   GstCapsFeatures *caps_features;
2190   ID3D11Device *device_handle;
2191   ID3D11DeviceContext *context_handle;
2192   /* *INDENT-OFF* */
2193   ComPtr<ID3D11VideoDevice> video_device;
2194   ComPtr<ID3D11VideoContext> video_context;
2195   ComPtr<ID3D11VideoProcessorEnumerator> video_proc_enum;
2196   ComPtr<ID3D11VideoProcessorEnumerator1> video_proc_enum1;
2197   /* *INDENT-ON* */
2198   HRESULT hr;
2199   D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
2200   D3D11_VIDEO_PROCESSOR_CAPS proc_caps = { 0, };
2201   UINT supported_methods = 0;
2202   GstD3D11DeinterlaceMethod default_method;
2203   gboolean blend;
2204   gboolean bob;
2205   gboolean adaptive;
2206   gboolean mocomp;
2207   /* NOTE: processor might be able to handle other formats.
2208    * However, not all YUV formats can be used for render target.
2209    * For instance, DXGI_FORMAT_Y210 and DXGI_FORMAT_Y410 formats cannot be
2210    * render target. In practice, interlaced stream would output of video
2211    * decoders, so NV12/P010/P016 can cover most of real-world use case.
2212    */
2213   DXGI_FORMAT formats_to_check[] = {
2214     DXGI_FORMAT_NV12,           /* NV12 */
2215     DXGI_FORMAT_P010,           /* P010_10LE */
2216     DXGI_FORMAT_P016,           /* P016_LE */
2217   };
2218   GValue *supported_formats = NULL;
2219   GstD3D11DeinterlaceClassData *cdata;
2220   guint max_past_frames = 0;
2221   guint max_future_frames = 0;
2222   guint i;
2223
2224   device_handle = gst_d3d11_device_get_device_handle (device);
2225   context_handle = gst_d3d11_device_get_device_context_handle (device);
2226
2227   hr = device_handle->QueryInterface (IID_PPV_ARGS (&video_device));
2228   if (!gst_d3d11_result (hr, device))
2229     return;
2230
2231   hr = context_handle->QueryInterface (IID_PPV_ARGS (&video_context));
2232   if (!gst_d3d11_result (hr, device))
2233     return;
2234
2235   memset (&desc, 0, sizeof (D3D11_VIDEO_PROCESSOR_CONTENT_DESC));
2236   desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
2237   desc.InputWidth = 320;
2238   desc.InputHeight = 240;
2239   desc.OutputWidth = 320;
2240   desc.OutputHeight = 240;
2241   desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
2242
2243   hr = video_device->CreateVideoProcessorEnumerator (&desc, &video_proc_enum);
2244   if (!gst_d3d11_result (hr, device))
2245     return;
2246
2247   /* We need ID3D11VideoProcessorEnumerator1 interface to check conversion
2248    * capability of device via CheckVideoProcessorFormatConversion()  */
2249   hr = video_proc_enum.As (&video_proc_enum1);
2250   if (!gst_d3d11_result (hr, device))
2251     return;
2252
2253   hr = video_proc_enum->GetVideoProcessorCaps (&proc_caps);
2254   if (!gst_d3d11_result (hr, device))
2255     return;
2256
2257   for (i = 0; i < proc_caps.RateConversionCapsCount; i++) {
2258     D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rate_conv_caps = { 0, };
2259
2260     hr = video_proc_enum->GetVideoProcessorRateConversionCaps (i,
2261         &rate_conv_caps);
2262     if (FAILED (hr))
2263       continue;
2264
2265     supported_methods |= rate_conv_caps.ProcessorCaps;
2266     max_past_frames = MAX (max_past_frames, rate_conv_caps.PastFrames);
2267     max_future_frames = MAX (max_future_frames, rate_conv_caps.FutureFrames);
2268   }
2269
2270   if (supported_methods == 0)
2271     return;
2272
2273 #define IS_SUPPORTED_METHOD(flags,val) (flags & val) == val
2274   blend = IS_SUPPORTED_METHOD (supported_methods,
2275       GST_D3D11_DEINTERLACE_METHOD_BLEND);
2276   bob = IS_SUPPORTED_METHOD (supported_methods,
2277       GST_D3D11_DEINTERLACE_METHOD_BOB);
2278   adaptive = IS_SUPPORTED_METHOD (supported_methods,
2279       GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE);
2280   mocomp = IS_SUPPORTED_METHOD (supported_methods,
2281       GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION);
2282 #undef IS_SUPPORTED_METHOD
2283
2284   if (!blend && !bob && !adaptive && !mocomp)
2285     return;
2286
2287   /* Drop all not supported methods from flags */
2288   supported_methods = supported_methods &
2289       (GST_D3D11_DEINTERLACE_METHOD_BLEND | GST_D3D11_DEINTERLACE_METHOD_BOB |
2290       GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE |
2291       GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION);
2292
2293   /* Prefer bob, it's equivalent to "linear" which is default mode of
2294    * software deinterlace element, also it's fallback mode
2295    * for our "adaptive" and "mocomp" modes. Note that since Direct3D12, "blend"
2296    * mode is no more supported, instead "bob" and "custom" mode are suported
2297    * by Direct3D12 */
2298   if (bob) {
2299     default_method = GST_D3D11_DEINTERLACE_METHOD_BOB;
2300   } else if (adaptive) {
2301     default_method = GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE;
2302   } else if (mocomp) {
2303     default_method = GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION;
2304   } else if (blend) {
2305     default_method = GST_D3D11_DEINTERLACE_METHOD_BLEND;
2306   } else {
2307     /* Programming error */
2308     g_return_if_reached ();
2309   }
2310
2311   for (i = 0; i < G_N_ELEMENTS (formats_to_check); i++) {
2312     UINT flags = 0;
2313     GValue val = G_VALUE_INIT;
2314     GstVideoFormat format;
2315     BOOL supported = FALSE;
2316
2317     hr = video_proc_enum->CheckVideoProcessorFormat (formats_to_check[i],
2318         &flags);
2319     if (FAILED (hr))
2320       continue;
2321
2322     /* D3D11 video processor can support other conversion at once,
2323      * including color format conversion.
2324      * But not all combinations of in/out pairs can be supported.
2325      * To make things simple, this element will do only deinterlacing
2326      * (might not be optimal in terms of processing power/resource though) */
2327
2328     /* D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT = 0x1,
2329      * D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT = 0x2,
2330      * MinGW header might not be defining the above enum values */
2331     if ((flags & 0x3) != 0x3)
2332       continue;
2333
2334     format = gst_d3d11_dxgi_format_to_gst (formats_to_check[i]);
2335     /* This is programming error! */
2336     if (format == GST_VIDEO_FORMAT_UNKNOWN) {
2337       GST_ERROR ("Couldn't convert DXGI format %d to video format",
2338           formats_to_check[i]);
2339       continue;
2340     }
2341
2342     hr = video_proc_enum1->CheckVideoProcessorFormatConversion
2343         (formats_to_check[i], DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
2344         formats_to_check[i], DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
2345         &supported);
2346     if (FAILED (hr) || !supported)
2347       continue;
2348
2349     if (!supported_formats) {
2350       supported_formats = g_new0 (GValue, 1);
2351       g_value_init (supported_formats, GST_TYPE_LIST);
2352     }
2353
2354     if (formats_to_check[i] == DXGI_FORMAT_P016) {
2355       /* This is used for P012 as well */
2356       g_value_init (&val, G_TYPE_STRING);
2357       g_value_set_static_string (&val,
2358           gst_video_format_to_string (GST_VIDEO_FORMAT_P012_LE));
2359       gst_value_list_append_and_take_value (supported_formats, &val);
2360     }
2361
2362     g_value_init (&val, G_TYPE_STRING);
2363     g_value_set_static_string (&val, gst_video_format_to_string (format));
2364     gst_value_list_append_and_take_value (supported_formats, &val);
2365   }
2366
2367   if (!supported_formats)
2368     return;
2369
2370   caps = gst_caps_new_empty_simple ("video/x-raw");
2371   /* FIXME: Check supported resolution, it would be different from
2372    * supported max texture dimension */
2373   gst_caps_set_simple (caps,
2374       "width", GST_TYPE_INT_RANGE, 1, 8192,
2375       "height", GST_TYPE_INT_RANGE, 1, 8192, NULL);
2376   gst_caps_set_value (caps, "format", supported_formats);
2377   g_value_unset (supported_formats);
2378   g_free (supported_formats);
2379
2380   /* TODO: Add alternating deinterlace */
2381   src_caps = gst_caps_copy (caps);
2382   caps_features = gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
2383       NULL);
2384   gst_caps_set_features_simple (src_caps, caps_features);
2385
2386   caps_features = gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
2387       GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, NULL);
2388   gst_caps_set_features_simple (caps, caps_features);
2389   gst_caps_append (src_caps, caps);
2390
2391   sink_caps = gst_caps_copy (src_caps);
2392
2393   GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
2394   GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
2395
2396   cdata = gst_d3d11_deinterlace_class_data_new ();
2397   cdata->sink_caps = sink_caps;
2398   cdata->src_caps = src_caps;
2399   cdata->device_caps.supported_methods =
2400       (GstD3D11DeinterlaceMethod) supported_methods;
2401   cdata->device_caps.default_method = default_method;
2402   cdata->device_caps.max_past_frames = max_past_frames;
2403   cdata->device_caps.max_future_frames = max_future_frames;
2404
2405   g_object_get (device, "adapter", &cdata->adapter,
2406       "device-id", &cdata->device_id, "vendor-id", &cdata->vendor_id,
2407       "description", &cdata->description, NULL);
2408   type_info.class_data = cdata;
2409   bin_type_info.class_data = gst_d3d11_deinterlace_class_data_ref (cdata);
2410
2411   type_name = g_strdup ("GstD3D11Deinterlace");
2412   feature_name = g_strdup ("d3d11deinterlaceelement");
2413
2414   while (g_type_from_name (type_name)) {
2415     index++;
2416     g_free (type_name);
2417     g_free (feature_name);
2418     type_name = g_strdup_printf ("GstD3D11Device%dDeinterlace", index);
2419     feature_name = g_strdup_printf ("d3d11device%ddeinterlaceelement", index);
2420   }
2421
2422   type = g_type_register_static (GST_TYPE_BASE_TRANSFORM,
2423       type_name, &type_info, (GTypeFlags) 0);
2424   cdata->deinterlace_type = type;
2425
2426   if (index != 0)
2427     gst_element_type_set_skip_documentation (type);
2428
2429   if (!gst_element_register (plugin, feature_name, GST_RANK_NONE, type))
2430     GST_WARNING ("Failed to register plugin '%s'", type_name);
2431
2432   g_free (type_name);
2433   g_free (feature_name);
2434
2435   /* Register wrapper bin */
2436   index = 0;
2437   type_name = g_strdup ("GstD3D11DeinterlaceBin");
2438   feature_name = g_strdup ("d3d11deinterlace");
2439
2440   while (g_type_from_name (type_name)) {
2441     index++;
2442     g_free (type_name);
2443     g_free (feature_name);
2444     type_name = g_strdup_printf ("GstD3D11Device%dDeinterlaceBin", index);
2445     feature_name = g_strdup_printf ("d3d11device%ddeinterlace", index);
2446   }
2447
2448   bin_type = g_type_register_static (GST_TYPE_BIN,
2449       type_name, &bin_type_info, (GTypeFlags) 0);
2450
2451   /* make lower rank than default device */
2452   if (rank > 0 && index != 0)
2453     rank--;
2454
2455   if (index != 0)
2456     gst_element_type_set_skip_documentation (bin_type);
2457
2458   if (!gst_element_register (plugin, feature_name, rank, bin_type))
2459     GST_WARNING ("Failed to register plugin '%s'", type_name);
2460
2461   g_free (type_name);
2462   g_free (feature_name);
2463 }