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