summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorSeungha Yang <seungha@centricular.com>2021-01-22 00:10:28 +0900
committerSeungha Yang <seungha@centricular.com>2021-02-17 03:52:19 +0900
commitcfbc580575bdebbccbaa78d39a6cb87607d1edfe (patch)
treeceb4613f0179f9673e2fb86f9050682a5f1e503c /sys
parent0ee13755a8e2ae5ec5422adbdb13fecbe8e72bb6 (diff)
downloadgstreamer-plugins-bad-cfbc580575bdebbccbaa78d39a6cb87607d1edfe.tar.gz
d3d11: Add support for deinterlacing by using ID3D11VideoProcessor interface
Add a new element d3d11deinterlace to support deinterlacing. Similar to d3d11videosink and d3d11compositor, this element is a wrapper bin of set of child elements including helpful conversion elements (upload/download and color convert) to make this element configurable between non-d3d11 elements. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2016>
Diffstat (limited to 'sys')
-rw-r--r--sys/d3d11/gstd3d11deinterlace.cpp2346
-rw-r--r--sys/d3d11/gstd3d11deinterlace.h34
-rw-r--r--sys/d3d11/meson.build6
-rw-r--r--sys/d3d11/plugin.c23
4 files changed, 2409 insertions, 0 deletions
diff --git a/sys/d3d11/gstd3d11deinterlace.cpp b/sys/d3d11/gstd3d11deinterlace.cpp
new file mode 100644
index 000000000..85b5b5261
--- /dev/null
+++ b/sys/d3d11/gstd3d11deinterlace.cpp
@@ -0,0 +1,2346 @@
+/* GStreamer
+ * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/video/video.h>
+#include <gst/base/gstbasetransform.h>
+
+#include "gstd3d11deinterlace.h"
+#include "gstd3d11pluginutils.h"
+#include <wrl.h>
+#include <string.h>
+
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+
+G_BEGIN_DECLS
+
+GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_deinterlace_debug);
+#define GST_CAT_DEFAULT gst_d3d11_deinterlace_debug
+
+G_END_DECLS
+/* *INDENT-ON* */
+
+/* Deinterlacing Methods:
+ * Direct3D11 provides Blend, Bob, Adaptive, Motion Compensation, and
+ * Inverse Telecine methods. But depending on video processor device,
+ * some of method might not be supported.
+ * - Blend: the two fields of a interlaced frame are blended into a single
+ * progressive frame. Output rate will be half of input (e.g., 60i -> 30p)
+ * but due to the way of framerate signalling of GStreamer, that is, it uses
+ * frame rate, not field rate for interlaced stream, in/output framerate
+ * of caps will be identical.
+ * - Bob: missing field lines are interpolated from the lines above and below.
+ * Output rate will be the same as that of input (e.g., 60i -> 60p).
+ * In order words, video processor will generate two frames from two field
+ * of a intelaced frame.
+ * - Adaptive, Motion Compensation: future and past frames are used for
+ * reference frame for deinterlacing process. User should provide sufficent
+ * number of reference frames, otherwise processor device will fallback to
+ * Bob method.
+ *
+ * Direct3D11 doesn't provide a method for explicit deinterlacing method
+ * selection. Instead, it could be done indirectly.
+ * - Blend: sets output rate as half via VideoProcessorSetStreamOutputRate().
+ * - Bob: sets output rate as normal. And performs VideoProcessorBlt() twice per
+ * a interlaced frame. D3D11_VIDEO_PROCESSOR_STREAM::OutputIndex needs to be
+ * incremented per field (e.g., OutputIndex = 0 for the first field,
+ * and 1 for the second field).
+ * - Adaptive, Motion Compensation: in addition to the requirement of Bob,
+ * user should provide reference frames via
+ * D3D11_VIDEO_PROCESSOR_STREAM::ppPastSurfaces and
+ * D3D11_VIDEO_PROCESSOR_STREAM::ppFutureSurfaces
+ */
+
+/* g_queue_clear_full is available since 2.60 */
+#if !GLIB_CHECK_VERSION(2,60,0)
+#define g_queue_clear_full gst_d3d11_deinterlace_g_queue_clear_full
+static void
+gst_d3d11_deinterlace_g_queue_clear_full (GQueue * queue,
+ GDestroyNotify free_func)
+{
+ g_return_if_fail (queue != NULL);
+
+ if (free_func != NULL)
+ g_queue_foreach (queue, (GFunc) free_func, NULL);
+
+ g_queue_clear (queue);
+}
+#endif
+
+typedef enum
+{
+ GST_D3D11_DEINTERLACE_METHOD_BLEND =
+ D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND,
+ GST_D3D11_DEINTERLACE_METHOD_BOB =
+ D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB,
+ GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE =
+ D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE,
+ GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION =
+ D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION,
+
+ /* TODO: INVERSE_TELECINE */
+} GstD3D11DeinterlaceMethod;
+
+#define GST_TYPE_D3D11_DEINTERLACE_METHOD (gst_d3d11_deinterlace_method_type())
+
+static GType
+gst_d3d11_deinterlace_method_type (void)
+{
+ static volatile gsize method_type = 0;
+
+ if (g_once_init_enter (&method_type)) {
+ static const GFlagsValue method_types[] = {
+ {GST_D3D11_DEINTERLACE_METHOD_BLEND,
+ "Blend: Blending top/bottom field pictures into one frame. "
+ "Framerate will be preserved (e.g., 60i -> 30p)", "blend"},
+ {GST_D3D11_DEINTERLACE_METHOD_BOB,
+ "Bob: Interpolating missing lines by using the adjacent lines. "
+ "Framerate will be doubled (e,g, 60i -> 60p)", "bob"},
+ {GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE,
+ "Adaptive: Interpolating missing lines by using spatial/temporal references. "
+ "Framerate will be doubled (e,g, 60i -> 60p)",
+ "adaptive"},
+ {GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION,
+ "Motion Compensation: Recreating missing lines by using motion vector. "
+ "Framerate will be doubled (e,g, 60i -> 60p)", "mocomp"},
+ {0, NULL, NULL},
+ };
+ GType tmp = g_flags_register_static ("GstD3D11DeinterlaceMethod",
+ method_types);
+ g_once_init_leave (&method_type, tmp);
+ }
+
+ return (GType) method_type;
+}
+
+typedef struct
+{
+ GstD3D11DeinterlaceMethod supported_methods;
+ GstD3D11DeinterlaceMethod default_method;
+
+ guint max_past_frames;
+ guint max_future_frames;
+} GstD3D11DeinterlaceDeviceCaps;
+
+typedef struct
+{
+ GType deinterlace_type;
+
+ GstCaps *sink_caps;
+ GstCaps *src_caps;
+ guint adapter;
+ guint device_id;
+ guint vendor_id;
+ gchar *description;
+
+ GstD3D11DeinterlaceDeviceCaps device_caps;
+
+ guint ref_count;
+} GstD3D11DeinterlaceClassData;
+
+static GstD3D11DeinterlaceClassData *
+gst_d3d11_deinterlace_class_data_new (void)
+{
+ GstD3D11DeinterlaceClassData *self = g_new0 (GstD3D11DeinterlaceClassData, 1);
+
+ self->ref_count = 1;
+
+ return self;
+}
+
+static GstD3D11DeinterlaceClassData *
+gst_d3d11_deinterlace_class_data_ref (GstD3D11DeinterlaceClassData * data)
+{
+ g_assert (data != NULL);
+
+ g_atomic_int_add (&data->ref_count, 1);
+
+ return data;
+}
+
+static void
+gst_d3d11_deinterlace_class_data_unref (GstD3D11DeinterlaceClassData * data)
+{
+ g_assert (data != NULL);
+
+ if (g_atomic_int_dec_and_test (&data->ref_count)) {
+ gst_clear_caps (&data->sink_caps);
+ gst_clear_caps (&data->src_caps);
+ g_free (data->description);
+ g_free (data);
+ }
+}
+
+enum
+{
+ PROP_0,
+ PROP_ADAPTER,
+ PROP_DEVICE_ID,
+ PROP_VENDOR_ID,
+ PROP_METHOD,
+ PROP_SUPPORTED_METHODS,
+};
+
+/* hardcoded maximum queue size for each past/future frame queue */
+#define MAX_NUM_REFERENCES 2
+
+typedef struct _GstD3D11Deinterlace
+{
+ GstBaseTransform parent;
+
+ GstVideoInfo in_info;
+ GstVideoInfo out_info;
+ /* Calculated buffer duration by using upstream framerate */
+ GstClockTime default_buffer_duration;
+
+ GstD3D11Device *device;
+
+ ID3D11VideoDevice *video_device;
+ ID3D11VideoContext *video_context;
+ ID3D11VideoProcessorEnumerator *video_enum;
+ ID3D11VideoProcessor *video_proc;
+
+ GstD3D11DeinterlaceMethod method;
+
+ GRecMutex lock;
+ GQueue past_frame_queue;
+ GQueue future_frame_queue;
+ GstBuffer *to_process;
+
+ guint max_past_frames;
+ guint max_future_frames;
+
+ /* D3D11_VIDEO_PROCESSOR_STREAM::InputFrameOrField */
+ guint input_index;
+
+ /* Clear/Update per submit_input_buffer() */
+ guint num_output_per_input;
+ guint num_transformed;
+ gboolean first_output;
+
+ GstBufferPool *fallback_in_pool;
+ GstBufferPool *fallback_out_pool;
+} GstD3D11Deinterlace;
+
+typedef struct _GstD3D11DeinterlaceClass
+{
+ GstBaseTransformClass parent_class;
+
+ guint adapter;
+ guint device_id;
+ guint vendor_id;
+
+ GstD3D11DeinterlaceDeviceCaps device_caps;
+} GstD3D11DeinterlaceClass;
+
+static GstElementClass *parent_class = NULL;
+
+#define GST_D3D11_DEINTERLACE(object) ((GstD3D11Deinterlace *) (object))
+#define GST_D3D11_DEINTERLACE_GET_CLASS(object) \
+ (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object), \
+ GstD3D11DeinterlaceClass))
+#define GST_D3D11_DEINTERLACE_LOCK(self) \
+ g_rec_mutex_lock (&GST_D3D11_DEINTERLACE (self)->lock);
+#define GST_D3D11_DEINTERLACE_UNLOCK(self) \
+ g_rec_mutex_unlock (&GST_D3D11_DEINTERLACE (self)->lock);
+
+static gboolean
+gst_d3d11_deinterlace_update_method (GstD3D11Deinterlace * self);
+static void gst_d3d11_deinterlace_reset (GstD3D11Deinterlace * self);
+static GstFlowReturn gst_d3d11_deinterlace_drain (GstD3D11Deinterlace * self);
+
+/* GObjectClass vfunc */
+static void gst_d3d11_deinterlace_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_d3d11_deinterlace_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_d3d11_deinterlace_finalize (GObject * object);
+
+/* GstElementClass vfunc */
+static void gst_d3d11_deinterlace_set_context (GstElement * element,
+ GstContext * context);
+
+/* GstBaseTransformClass vfunc */
+static gboolean gst_d3d11_deinterlace_start (GstBaseTransform * trans);
+static gboolean gst_d3d11_deinterlace_stop (GstBaseTransform * trans);
+static gboolean gst_d3d11_deinterlace_query (GstBaseTransform * trans,
+ GstPadDirection direction, GstQuery * query);
+static GstCaps *gst_d3d11_deinterlace_transform_caps (GstBaseTransform * trans,
+ GstPadDirection direction, GstCaps * caps, GstCaps * filter);
+static GstCaps *gst_d3d11_deinterlace_fixate_caps (GstBaseTransform * trans,
+ GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
+static gboolean
+gst_d3d11_deinterlace_propose_allocation (GstBaseTransform * trans,
+ GstQuery * decide_query, GstQuery * query);
+static gboolean
+gst_d3d11_deinterlace_decide_allocation (GstBaseTransform * trans,
+ GstQuery * query);
+static gboolean gst_d3d11_deinterlace_set_caps (GstBaseTransform * trans,
+ GstCaps * incaps, GstCaps * outcaps);
+static GstFlowReturn
+gst_d3d11_deinterlace_submit_input_buffer (GstBaseTransform * trans,
+ gboolean is_discont, GstBuffer * input);
+static GstFlowReturn
+gst_d3d11_deinterlace_generate_output (GstBaseTransform * trans,
+ GstBuffer ** outbuf);
+static GstFlowReturn
+gst_d3d11_deinterlace_transform (GstBaseTransform * trans, GstBuffer * inbuf,
+ GstBuffer * outbuf);
+static gboolean gst_d3d11_deinterlace_sink_event (GstBaseTransform * trans,
+ GstEvent * event);
+
+static void
+gst_d3d11_deinterlace_class_init (GstD3D11DeinterlaceClass * klass,
+ gpointer data)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
+ GstD3D11DeinterlaceClassData *cdata = (GstD3D11DeinterlaceClassData *) data;
+ gchar *long_name;
+
+ parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
+
+ gobject_class->get_property = gst_d3d11_deinterlace_get_property;
+ gobject_class->set_property = gst_d3d11_deinterlace_set_property;
+ gobject_class->finalize = gst_d3d11_deinterlace_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_ADAPTER,
+ g_param_spec_uint ("adapter", "Adapter",
+ "DXGI Adapter index for creating device",
+ 0, G_MAXUINT32, cdata->adapter,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
+ g_param_spec_uint ("device-id", "Device Id",
+ "DXGI Device ID", 0, G_MAXUINT32, 0,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_VENDOR_ID,
+ g_param_spec_uint ("vendor-id", "Vendor Id",
+ "DXGI Vendor ID", 0, G_MAXUINT32, 0,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_METHOD,
+ g_param_spec_flags ("method", "Method",
+ "Deinterlace Method. Use can set multiple methods as a flagset "
+ "and element will select one of method automatically. "
+ "If deinterlacing device failed to deinterlace with given mode, "
+ "fallback might happen by the device",
+ GST_TYPE_D3D11_DEINTERLACE_METHOD, cdata->device_caps.default_method,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY)));
+ g_object_class_install_property (gobject_class, PROP_SUPPORTED_METHODS,
+ g_param_spec_flags ("supported-methods", "Supported Methods",
+ "Set of supported deinterlace methods by device",
+ GST_TYPE_D3D11_DEINTERLACE_METHOD,
+ cdata->device_caps.supported_methods,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ element_class->set_context =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_set_context);
+
+ long_name = g_strdup_printf ("Direct3D11 %s Deinterlacer",
+ cdata->description);
+ gst_element_class_set_metadata (element_class, long_name,
+ "Filter/Effect/Video/Deinterlace/Hardware",
+ "A Direct3D11 based deinterlacer",
+ "Seungha Yang <seungha@centricular.com>");
+ g_free (long_name);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ cdata->sink_caps));
+ gst_element_class_add_pad_template (element_class,
+ gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ cdata->src_caps));
+
+ trans_class->passthrough_on_same_caps = TRUE;
+
+ trans_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_start);
+ trans_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_stop);
+ trans_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_query);
+ trans_class->transform_caps =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_transform_caps);
+ trans_class->fixate_caps =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_fixate_caps);
+ trans_class->propose_allocation =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_propose_allocation);
+ trans_class->decide_allocation =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_decide_allocation);
+ trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_set_caps);
+ trans_class->submit_input_buffer =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_submit_input_buffer);
+ trans_class->generate_output =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_generate_output);
+ trans_class->transform = GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_transform);
+ trans_class->sink_event =
+ GST_DEBUG_FUNCPTR (gst_d3d11_deinterlace_sink_event);
+
+ klass->adapter = cdata->adapter;
+ klass->device_id = cdata->device_id;
+ klass->vendor_id = cdata->vendor_id;
+ klass->device_caps = cdata->device_caps;
+
+ gst_d3d11_deinterlace_class_data_unref (cdata);
+}
+
+static void
+gst_d3d11_deinterlace_init (GstD3D11Deinterlace * self)
+{
+ GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
+
+ self->method = klass->device_caps.default_method;
+ self->default_buffer_duration = GST_CLOCK_TIME_NONE;
+ gst_d3d11_deinterlace_update_method (self);
+
+ g_queue_init (&self->past_frame_queue);
+ g_queue_init (&self->future_frame_queue);
+ g_rec_mutex_init (&self->lock);
+}
+
+static void
+gst_d3d11_deinterlace_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
+ GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (object);
+
+ switch (prop_id) {
+ case PROP_ADAPTER:
+ g_value_set_uint (value, klass->adapter);
+ break;
+ case PROP_DEVICE_ID:
+ g_value_set_uint (value, klass->device_id);
+ break;
+ case PROP_VENDOR_ID:
+ g_value_set_uint (value, klass->vendor_id);
+ break;
+ case PROP_METHOD:
+ g_value_set_flags (value, self->method);
+ break;
+ case PROP_SUPPORTED_METHODS:
+ g_value_set_flags (value, klass->device_caps.supported_methods);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gst_d3d11_deinterlace_update_method (GstD3D11Deinterlace * self)
+{
+ GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
+ GstD3D11DeinterlaceMethod requested_method = self->method;
+ gboolean updated = TRUE;
+
+ /* Verify whether requested method is supported */
+ if ((self->method & klass->device_caps.supported_methods) == 0) {
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar *supported, *requested;
+
+ supported = g_flags_to_string (GST_TYPE_D3D11_DEINTERLACE_METHOD,
+ klass->device_caps.supported_methods);
+ requested = g_flags_to_string (GST_TYPE_D3D11_DEINTERLACE_METHOD,
+ klass->device_caps.supported_methods);
+
+ GST_WARNING_OBJECT (self,
+ "Requested method %s is not supported (supported: %s)",
+ requested, supported);
+
+ g_free (supported);
+ g_free (requested);
+#endif
+
+ self->method = klass->device_caps.default_method;
+
+ goto done;
+ }
+
+ /* Drop not supported methods */
+ self->method = (GstD3D11DeinterlaceMethod)
+ (klass->device_caps.supported_methods & self->method);
+
+ /* Single method was requested? */
+ if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND ||
+ self->method == GST_D3D11_DEINTERLACE_METHOD_BOB ||
+ self->method == GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE ||
+ self->method == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
+ if (self->method == requested_method)
+ updated = FALSE;
+ } else {
+ /* Pick single method from requested */
+ if ((self->method & GST_D3D11_DEINTERLACE_METHOD_BOB) ==
+ GST_D3D11_DEINTERLACE_METHOD_BOB) {
+ self->method = GST_D3D11_DEINTERLACE_METHOD_BOB;
+ } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE) ==
+ GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE) {
+ self->method = GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE;
+ } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION)
+ == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
+ self->method = GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION;
+ } else if ((self->method & GST_D3D11_DEINTERLACE_METHOD_BLEND) ==
+ GST_D3D11_DEINTERLACE_METHOD_BLEND) {
+ self->method = GST_D3D11_DEINTERLACE_METHOD_BLEND;
+ } else {
+ self->method = klass->device_caps.default_method;
+ g_assert_not_reached ();
+ }
+ }
+
+done:
+ if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND) {
+ /* Both methods don't use reference frame for deinterlacing */
+ self->max_past_frames = self->max_future_frames = 0;
+ } else if (self->method == GST_D3D11_DEINTERLACE_METHOD_BOB) {
+ /* To calculate timestamp and duration of output fraems, we will hold one
+ * future frame even though processor device will not use reference */
+ self->max_past_frames = 0;
+ self->max_future_frames = 1;
+ } else {
+ /* FIXME: how many frames should be allowed? also, this needs to be
+ * configurable */
+ self->max_past_frames = MIN (klass->device_caps.max_past_frames,
+ MAX_NUM_REFERENCES);
+
+ /* Likewise Bob, we need at least one future frame for timestamp/duration
+ * calculation */
+ self->max_future_frames =
+ MAX (MIN (klass->device_caps.max_future_frames, MAX_NUM_REFERENCES), 1);
+ }
+
+ return updated;
+}
+
+static void
+gst_d3d11_deinterlace_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
+
+ switch (prop_id) {
+ case PROP_METHOD:{
+ gboolean notify_update = FALSE;
+
+ GST_OBJECT_LOCK (self);
+ self->method = (GstD3D11DeinterlaceMethod) g_value_get_flags (value);
+ notify_update = gst_d3d11_deinterlace_update_method (self);
+ GST_OBJECT_UNLOCK (self);
+
+ if (notify_update)
+ g_object_notify (object, "method");
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_d3d11_deinterlace_finalize (GObject * object)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (object);
+
+ g_rec_mutex_clear (&self->lock);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_d3d11_deinterlace_set_context (GstElement * element, GstContext * context)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (element);
+ GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
+
+ gst_d3d11_handle_set_context (element, context, klass->adapter,
+ &self->device);
+
+ GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
+}
+
+static gboolean
+gst_d3d11_deinterlace_open (GstD3D11Deinterlace * self)
+{
+ HRESULT hr;
+ ID3D11Device *device_handle;
+ ID3D11DeviceContext *context_handle;
+ /* *INDENT-OFF* */
+ ComPtr<ID3D11VideoDevice> video_device;
+ ComPtr<ID3D11VideoContext> video_context;
+ /* *INDENT-ON* */
+
+ device_handle = gst_d3d11_device_get_device_handle (self->device);
+ context_handle = gst_d3d11_device_get_device_context_handle (self->device);
+
+ hr = device_handle->QueryInterface (IID_PPV_ARGS (&video_device));
+ if (!gst_d3d11_result (hr, self->device))
+ return FALSE;
+
+ hr = context_handle->QueryInterface (IID_PPV_ARGS (&video_context));
+ if (!gst_d3d11_result (hr, self->device))
+ return FALSE;
+
+ self->video_device = video_device.Detach ();
+ self->video_context = video_context.Detach ();
+
+ return TRUE;
+}
+
+/* Must be called with lock taken */
+static void
+gst_d3d11_deinterlace_reset_history (GstD3D11Deinterlace * self)
+{
+ self->input_index = 0;
+ self->num_output_per_input = 1;
+ self->num_transformed = 0;
+ self->first_output = TRUE;
+
+ g_queue_clear_full (&self->past_frame_queue,
+ (GDestroyNotify) gst_buffer_unref);
+ g_queue_clear_full (&self->future_frame_queue,
+ (GDestroyNotify) gst_buffer_unref);
+ gst_clear_buffer (&self->to_process);
+}
+
+static void
+gst_d3d11_deinterlace_reset (GstD3D11Deinterlace * self)
+{
+ GST_D3D11_DEINTERLACE_LOCK (self);
+ if (self->fallback_in_pool) {
+ gst_buffer_pool_set_active (self->fallback_in_pool, FALSE);
+ gst_object_unref (self->fallback_in_pool);
+ self->fallback_in_pool = NULL;
+ }
+
+ if (self->fallback_out_pool) {
+ gst_buffer_pool_set_active (self->fallback_out_pool, FALSE);
+ gst_object_unref (self->fallback_out_pool);
+ self->fallback_out_pool = NULL;
+ }
+
+ if (self->video_enum) {
+ self->video_enum->Release ();
+ self->video_enum = NULL;
+ }
+
+ if (self->video_proc) {
+ self->video_proc->Release ();
+ self->video_proc = NULL;
+ }
+
+ gst_d3d11_deinterlace_reset_history (self);
+ self->default_buffer_duration = GST_CLOCK_TIME_NONE;
+
+ GST_D3D11_DEINTERLACE_UNLOCK (self);
+}
+
+static void
+gst_d3d11_deinterlace_close (GstD3D11Deinterlace * self)
+{
+ gst_d3d11_deinterlace_reset (self);
+
+ if (self->video_device) {
+ self->video_device->Release ();
+ self->video_device = NULL;
+ }
+
+ if (self->video_context) {
+ self->video_context->Release ();
+ self->video_context = NULL;
+ }
+
+ gst_clear_object (&self->device);
+}
+
+static gboolean
+gst_d3d11_deinterlace_start (GstBaseTransform * trans)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstD3D11DeinterlaceClass *klass = GST_D3D11_DEINTERLACE_GET_CLASS (self);
+
+ if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self), klass->adapter,
+ &self->device)) {
+ GST_ERROR_OBJECT (self, "Couldn't create d3d11device");
+ return FALSE;
+ }
+
+ if (!gst_d3d11_deinterlace_open (self)) {
+ GST_ERROR_OBJECT (self, "Couldn't open video device");
+ gst_d3d11_deinterlace_close (self);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_d3d11_deinterlace_stop (GstBaseTransform * trans)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+
+ gst_d3d11_deinterlace_close (self);
+
+ return TRUE;
+}
+
+static gboolean
+gst_d3d11_deinterlace_query (GstBaseTransform * trans,
+ GstPadDirection direction, GstQuery * query)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CONTEXT:
+ if (gst_d3d11_handle_context_query (GST_ELEMENT_CAST (self),
+ query, self->device)) {
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
+ query);
+}
+
+static GstCaps *
+gst_d3d11_deinterlace_remove_interlace_info (GstCaps * caps,
+ gboolean remove_framerate)
+{
+ GstStructure *st;
+ GstCapsFeatures *f;
+ gint i, n;
+ GstCaps *res;
+ GstCapsFeatures *feature =
+ gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY);
+
+ res = gst_caps_new_empty ();
+
+ n = gst_caps_get_size (caps);
+ for (i = 0; i < n; i++) {
+ st = gst_caps_get_structure (caps, i);
+ f = gst_caps_get_features (caps, i);
+
+ /* If this is already expressed by the existing caps
+ * skip this structure */
+ if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
+ continue;
+
+ st = gst_structure_copy (st);
+ /* Only remove format info for the cases when we can actually convert */
+ if (!gst_caps_features_is_any (f)
+ && gst_caps_features_is_equal (f, feature)) {
+ if (remove_framerate) {
+ gst_structure_remove_fields (st, "interlace-mode", "field-order",
+ "framerate", NULL);
+ } else {
+ gst_structure_remove_fields (st, "interlace-mode", "field-order", NULL);
+ }
+ }
+
+ gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
+ }
+
+ gst_caps_features_free (feature);
+
+ return res;
+}
+
+static GstCaps *
+gst_d3d11_deinterlace_transform_caps (GstBaseTransform * trans,
+ GstPadDirection direction, GstCaps * caps, GstCaps * filter)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstCaps *tmp, *tmp2;
+ GstCaps *result;
+
+ /* Get all possible caps that we can transform to */
+ tmp = gst_d3d11_deinterlace_remove_interlace_info (caps,
+ /* Non-blend mode will double framerate */
+ self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND);
+
+ if (filter) {
+ tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
+ gst_caps_unref (tmp);
+ tmp = tmp2;
+ }
+
+ result = tmp;
+
+ GST_DEBUG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " into %"
+ GST_PTR_FORMAT, caps, result);
+
+ return result;
+}
+
+static GstCaps *
+gst_d3d11_deinterlace_fixate_caps (GstBaseTransform * trans,
+ GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstStructure *s;
+ GstCaps *tmp;
+ gint fps_n, fps_d;
+ GstVideoInfo info;
+ const gchar *interlace_mode;
+
+ othercaps = gst_caps_truncate (othercaps);
+ othercaps = gst_caps_make_writable (othercaps);
+
+ if (direction == GST_PAD_SRC)
+ return gst_caps_fixate (othercaps);
+
+ tmp = gst_caps_copy (caps);
+ tmp = gst_caps_fixate (tmp);
+
+ if (!gst_video_info_from_caps (&info, tmp)) {
+ GST_WARNING_OBJECT (self, "Invalid caps %" GST_PTR_FORMAT, caps);
+ gst_caps_unref (tmp);
+
+ return gst_caps_fixate (othercaps);
+ }
+
+ s = gst_caps_get_structure (tmp, 0);
+ if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
+ /* for non-blend method, output framerate will be doubled */
+ if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND &&
+ GST_VIDEO_INFO_IS_INTERLACED (&info)) {
+ fps_n *= 2;
+ }
+
+ gst_caps_set_simple (othercaps,
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
+ }
+
+ interlace_mode = gst_structure_get_string (s, "interlace-mode");
+ if (g_strcmp0 ("progressive", interlace_mode) == 0) {
+ /* Just forward interlace-mode=progressive.
+ * By this way, basetransform will enable passthrough for non-interlaced
+ * stream*/
+ gst_caps_set_simple (othercaps,
+ "interlace-mode", G_TYPE_STRING, "progressive", NULL);
+ }
+
+ gst_caps_unref (tmp);
+
+ return gst_caps_fixate (othercaps);
+}
+
+static gboolean
+gst_d3d11_deinterlace_propose_allocation (GstBaseTransform * trans,
+ GstQuery * decide_query, GstQuery * query)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstVideoInfo info;
+ GstBufferPool *pool = NULL;
+ GstCaps *caps;
+ guint n_pools, i;
+ GstStructure *config;
+ guint size;
+ GstD3D11AllocationParams *d3d11_params;
+ guint min_buffers = 0;
+
+ if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
+ decide_query, query))
+ return FALSE;
+
+ /* passthrough, we're done */
+ if (decide_query == NULL)
+ return TRUE;
+
+ gst_query_parse_allocation (query, &caps, NULL);
+
+ if (caps == NULL)
+ return FALSE;
+
+ if (!gst_video_info_from_caps (&info, caps))
+ return FALSE;
+
+ n_pools = gst_query_get_n_allocation_pools (query);
+ for (i = 0; i < n_pools; i++) {
+ gst_query_parse_nth_allocation_pool (query, i, &pool, NULL, NULL, NULL);
+ if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
+ gst_object_unref (pool);
+ pool = NULL;
+ }
+ }
+
+ if (!pool)
+ pool = gst_d3d11_buffer_pool_new (self->device);
+
+ config = gst_buffer_pool_get_config (pool);
+ gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+ d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
+ if (!d3d11_params) {
+ d3d11_params = gst_d3d11_allocation_params_new (self->device, &info,
+ (GstD3D11AllocationFlags) 0, D3D11_BIND_RENDER_TARGET);
+ } else {
+ d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
+ }
+
+ gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
+ gst_d3d11_allocation_params_free (d3d11_params);
+
+ if (self->method == GST_D3D11_DEINTERLACE_METHOD_BOB) {
+ /* For non-blend methods, we will produce two progressive frames from
+ * a single interlaced frame. To determine timestamp and duration,
+ * we might need to hold one past frame if buffer duration is unknown */
+ min_buffers = 2;
+ } else if (self->method == GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE ||
+ self->method == GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION) {
+ /* For advanced deinterlacing methods, we will hold more frame so that
+ * device can use them as reference frames */
+
+ min_buffers += self->max_past_frames;
+ min_buffers += self->max_future_frames;
+ /* And one for current frame */
+ min_buffers++;
+
+ /* we will hold at least one frame for timestamp/duration calculation */
+ min_buffers = MAX (min_buffers, 2);
+ }
+
+ /* size will be updated by d3d11 buffer pool */
+ gst_buffer_pool_config_set_params (config, caps, 0, min_buffers, 0);
+
+ if (!gst_buffer_pool_set_config (pool, config))
+ goto config_failed;
+
+ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
+ gst_query_add_allocation_meta (query,
+ GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
+
+ size = GST_D3D11_BUFFER_POOL (pool)->buffer_size;
+ gst_query_add_allocation_pool (query, pool, size, min_buffers, 0);
+
+ gst_object_unref (pool);
+
+ return TRUE;
+
+ /* ERRORS */
+config_failed:
+ {
+ GST_ERROR_OBJECT (self, "failed to set config");
+ gst_object_unref (pool);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_d3d11_deinterlace_decide_allocation (GstBaseTransform * trans,
+ GstQuery * query)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstCaps *outcaps = NULL;
+ GstBufferPool *pool = NULL;
+ guint size, min = 0, max = 0;
+ GstStructure *config;
+ GstD3D11AllocationParams *d3d11_params;
+ gboolean update_pool = FALSE;
+ GstVideoInfo info;
+
+ gst_query_parse_allocation (query, &outcaps, NULL);
+
+ if (!outcaps)
+ return FALSE;
+
+ if (!gst_video_info_from_caps (&info, outcaps))
+ return FALSE;
+
+ size = GST_VIDEO_INFO_SIZE (&info);
+
+ if (gst_query_get_n_allocation_pools (query) > 0) {
+ gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
+ if (pool && !GST_IS_D3D11_BUFFER_POOL (pool)) {
+ gst_object_unref (pool);
+ pool = NULL;
+ }
+
+ update_pool = TRUE;
+ }
+
+ if (!pool)
+ pool = gst_d3d11_buffer_pool_new (self->device);
+
+ config = gst_buffer_pool_get_config (pool);
+ gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+ d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
+ if (!d3d11_params) {
+ d3d11_params = gst_d3d11_allocation_params_new (self->device, &info,
+ (GstD3D11AllocationFlags) 0, D3D11_BIND_RENDER_TARGET);
+ } else {
+ d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
+ }
+
+ gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
+ gst_d3d11_allocation_params_free (d3d11_params);
+
+ gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
+ gst_buffer_pool_set_config (pool, config);
+
+ size = GST_D3D11_BUFFER_POOL (pool)->buffer_size;
+
+ if (update_pool)
+ gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
+ else
+ gst_query_add_allocation_pool (query, pool, size, min, max);
+
+ gst_object_unref (pool);
+
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
+ query);
+}
+
+static gboolean
+gst_d3d11_deinterlace_prepare_fallback_pool (GstD3D11Deinterlace * self,
+ GstCaps * in_caps, GstVideoInfo * in_info, GstCaps * out_caps,
+ GstVideoInfo * out_info)
+{
+ GstD3D11AllocationParams *d3d11_params;
+
+ /* Clearing potentially remaining resource here would be redundant.
+ * Just to be safe enough */
+ g_queue_clear_full (&self->past_frame_queue,
+ (GDestroyNotify) gst_buffer_unref);
+ g_queue_clear_full (&self->future_frame_queue,
+ (GDestroyNotify) gst_buffer_unref);
+
+ if (self->fallback_in_pool) {
+ gst_buffer_pool_set_active (self->fallback_in_pool, FALSE);
+ gst_object_unref (self->fallback_in_pool);
+ self->fallback_in_pool = NULL;
+ }
+
+ if (self->fallback_out_pool) {
+ gst_buffer_pool_set_active (self->fallback_out_pool, FALSE);
+ gst_object_unref (self->fallback_out_pool);
+ self->fallback_out_pool = NULL;
+ }
+
+ /* Empty bind flag is allowed for video processor input */
+ d3d11_params = gst_d3d11_allocation_params_new (self->device, in_info,
+ (GstD3D11AllocationFlags) 0, 0);
+ self->fallback_in_pool = gst_d3d11_buffer_pool_new_with_options (self->device,
+ in_caps, d3d11_params, 0, 0);
+ gst_d3d11_allocation_params_free (d3d11_params);
+
+ if (!self->fallback_in_pool) {
+ GST_ERROR_OBJECT (self, "Failed to create input fallback buffer pool");
+ return FALSE;
+ }
+
+ /* For processor output, render target bind flag is required */
+ d3d11_params = gst_d3d11_allocation_params_new (self->device, out_info,
+ (GstD3D11AllocationFlags) 0, D3D11_BIND_RENDER_TARGET);
+ self->fallback_out_pool =
+ gst_d3d11_buffer_pool_new_with_options (self->device,
+ out_caps, d3d11_params, 0, 0);
+ gst_d3d11_allocation_params_free (d3d11_params);
+
+ if (!self->fallback_out_pool) {
+ GST_ERROR_OBJECT (self, "Failed to create output fallback buffer pool");
+ gst_clear_object (&self->fallback_out_pool);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_d3d11_deinterlace_set_caps (GstBaseTransform * trans,
+ GstCaps * incaps, GstCaps * outcaps)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstVideoInfo in_info, out_info;
+ /* *INDENT-OFF* */
+ ComPtr<ID3D11VideoProcessorEnumerator> video_enum;
+ ComPtr<ID3D11VideoProcessor> video_proc;
+ /* *INDENT-ON* */
+ D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
+ D3D11_VIDEO_PROCESSOR_CAPS proc_caps;
+ D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rate_conv_caps;
+ D3D11_VIDEO_PROCESSOR_OUTPUT_RATE output_rate =
+ D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL;
+ HRESULT hr;
+ RECT rect;
+ guint i;
+
+ if (gst_base_transform_is_passthrough (trans))
+ return TRUE;
+
+ if (!gst_video_info_from_caps (&in_info, incaps)) {
+ GST_ERROR_OBJECT (self, "Invalid input caps %" GST_PTR_FORMAT, incaps);
+ return FALSE;
+ }
+
+ if (!gst_video_info_from_caps (&out_info, outcaps)) {
+ GST_ERROR_OBJECT (self, "Invalid output caps %" GST_PTR_FORMAT, outcaps);
+ return FALSE;
+ }
+
+ self->in_info = in_info;
+ self->out_info = out_info;
+
+ /* Calculate expected buffer duration. We might need to reference this value
+ * when buffer duration is unknown */
+ if (GST_VIDEO_INFO_FPS_N (&in_info) > 0 &&
+ GST_VIDEO_INFO_FPS_D (&in_info) > 0) {
+ self->default_buffer_duration =
+ gst_util_uint64_scale_int (GST_SECOND, GST_VIDEO_INFO_FPS_D (&in_info),
+ GST_VIDEO_INFO_FPS_N (&in_info));
+ } else {
+ /* Assume 25 fps. We need this for reporting latency at least */
+ self->default_buffer_duration =
+ gst_util_uint64_scale_int (GST_SECOND, 1, 25);
+ }
+
+ gst_d3d11_deinterlace_reset (self);
+
+ /* Nothing to do */
+ if (!GST_VIDEO_INFO_IS_INTERLACED (&in_info)) {
+ gst_base_transform_set_passthrough (trans, TRUE);
+
+ return TRUE;
+ }
+
+ /* TFF or BFF is not important here, this is just for enumerating
+ * available deinterlace devices */
+ memset (&desc, 0, sizeof (D3D11_VIDEO_PROCESSOR_CONTENT_DESC));
+
+ desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
+ if (GST_VIDEO_INFO_FIELD_ORDER (&in_info) ==
+ GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST)
+ desc.InputFrameFormat =
+ D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
+ desc.InputWidth = GST_VIDEO_INFO_WIDTH (&in_info);
+ desc.InputHeight = GST_VIDEO_INFO_HEIGHT (&in_info);
+ desc.OutputWidth = GST_VIDEO_INFO_WIDTH (&out_info);
+ desc.OutputHeight = GST_VIDEO_INFO_HEIGHT (&out_info);
+ desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
+
+ hr = self->video_device->CreateVideoProcessorEnumerator (&desc, &video_enum);
+ if (!gst_d3d11_result (hr, self->device)) {
+ GST_ERROR_OBJECT (self, "Couldn't create VideoProcessorEnumerator");
+ return FALSE;
+ }
+
+ hr = video_enum->GetVideoProcessorCaps (&proc_caps);
+ if (!gst_d3d11_result (hr, self->device)) {
+ GST_ERROR_OBJECT (self, "Couldn't query processor caps");
+ return FALSE;
+ }
+
+ /* Shouldn't happen, we checked this already during plugin_init */
+ if (proc_caps.RateConversionCapsCount == 0) {
+ GST_ERROR_OBJECT (self, "Deinterlacing is not supported");
+ return FALSE;
+ }
+
+ for (i = 0; i < proc_caps.RateConversionCapsCount; i++) {
+ hr = video_enum->GetVideoProcessorRateConversionCaps (i, &rate_conv_caps);
+ if (FAILED (hr))
+ continue;
+
+ if ((rate_conv_caps.ProcessorCaps & self->method) == self->method)
+ break;
+ }
+
+ if (i >= proc_caps.RateConversionCapsCount) {
+ GST_ERROR_OBJECT (self, "Deinterlacing method 0x%x is not supported",
+ self->method);
+ return FALSE;
+ }
+
+ hr = self->video_device->CreateVideoProcessor (video_enum.Get (),
+ i, &video_proc);
+ if (!gst_d3d11_result (hr, self->device)) {
+ GST_ERROR_OBJECT (self, "Couldn't create processor");
+ return FALSE;
+ }
+
+ if (!gst_d3d11_deinterlace_prepare_fallback_pool (self, incaps, &in_info,
+ outcaps, &out_info)) {
+ GST_ERROR_OBJECT (self, "Couldn't prepare fallback buffer pool");
+ return FALSE;
+ }
+
+ self->video_enum = video_enum.Detach ();
+ self->video_proc = video_proc.Detach ();
+
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = GST_VIDEO_INFO_WIDTH (&self->in_info);
+ rect.bottom = GST_VIDEO_INFO_HEIGHT (&self->in_info);
+
+ /* Blending seems to be considered as half rate. See also
+ * https://docs.microsoft.com/en-us/windows/win32/api/d3d12video/ns-d3d12video-d3d12_video_process_input_stream_rate */
+ if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND)
+ output_rate = D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_HALF;
+
+ gst_d3d11_device_lock (self->device);
+ self->video_context->VideoProcessorSetStreamSourceRect (self->video_proc,
+ 0, TRUE, &rect);
+ self->video_context->VideoProcessorSetStreamDestRect (self->video_proc,
+ 0, TRUE, &rect);
+ self->video_context->VideoProcessorSetOutputTargetRect (self->video_proc,
+ TRUE, &rect);
+ self->video_context->
+ VideoProcessorSetStreamAutoProcessingMode (self->video_proc, 0, FALSE);
+ self->video_context->VideoProcessorSetStreamOutputRate (self->video_proc, 0,
+ output_rate, TRUE, NULL);
+ gst_d3d11_device_unlock (self->device);
+
+ return TRUE;
+}
+
+static ID3D11VideoProcessorInputView *
+gst_d3d11_deinterace_get_piv_from_buffer (GstD3D11Deinterlace * self,
+ GstBuffer * buffer)
+{
+ GstMemory *mem;
+ GstD3D11Memory *dmem;
+ ID3D11VideoProcessorInputView *piv;
+
+ if (gst_buffer_n_memory (buffer) != 1) {
+ GST_WARNING_OBJECT (self, "Input buffer has more than one memory");
+ return NULL;
+ }
+
+ mem = gst_buffer_peek_memory (buffer, 0);
+ if (!gst_is_d3d11_memory (mem)) {
+ GST_WARNING_OBJECT (self, "Input buffer is holding non-D3D11 memory");
+ return NULL;
+ }
+
+ dmem = (GstD3D11Memory *) mem;
+ if (dmem->device != self->device) {
+ GST_WARNING_OBJECT (self,
+ "Input D3D11 memory was allocated by other device");
+ return NULL;
+ }
+
+ piv = gst_d3d11_memory_get_processor_input_view (dmem,
+ self->video_device, self->video_enum);
+ if (!piv) {
+ GST_WARNING_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
+ return NULL;
+ }
+
+ return piv;
+}
+
+static GstBuffer *
+gst_d3d11_deinterlace_ensure_input_buffer (GstD3D11Deinterlace * self,
+ GstBuffer * input)
+{
+ GstD3D11Memory *dmem;
+ ID3D11VideoProcessorInputView *piv;
+ GstBuffer *new_buf = NULL;
+
+ if (!input)
+ return NULL;
+
+ piv = gst_d3d11_deinterace_get_piv_from_buffer (self, input);
+ if (piv)
+ return input;
+
+ if (!self->fallback_in_pool ||
+ !gst_buffer_pool_set_active (self->fallback_in_pool, TRUE) ||
+ gst_buffer_pool_acquire_buffer (self->fallback_in_pool, &new_buf,
+ NULL) != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (self, "Fallback input buffer is unavailable");
+ gst_buffer_unref (input);
+
+ return NULL;
+ }
+
+ if (!gst_d3d11_buffer_copy_into (new_buf, input, &self->in_info)) {
+ GST_ERROR_OBJECT (self, "Couldn't copy input buffer to fallback buffer");
+ gst_buffer_unref (new_buf);
+ gst_buffer_unref (input);
+
+ return NULL;
+ }
+
+ dmem = (GstD3D11Memory *) gst_buffer_peek_memory (new_buf, 0);
+ piv = gst_d3d11_memory_get_processor_input_view (dmem,
+ self->video_device, self->video_enum);
+ if (!piv) {
+ GST_ERROR_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
+ gst_buffer_unref (new_buf);
+ gst_buffer_unref (input);
+
+ return NULL;
+ }
+
+ /* copy metadata, default implemenation of baseclass will copy everything
+ * what we need */
+ GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
+ (GST_BASE_TRANSFORM_CAST (self), input, new_buf);
+
+ gst_buffer_unref (input);
+
+ return new_buf;
+}
+
+static GstFlowReturn
+gst_d3d11_deinterlace_submit_future_frame (GstD3D11Deinterlace * self,
+ GstBuffer * buffer)
+{
+ GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (self);
+ guint len;
+
+ /* push tail and pop head, so that head frame can be the nearest frame
+ * of current frame */
+ if (buffer)
+ g_queue_push_tail (&self->future_frame_queue, buffer);
+
+ len = g_queue_get_length (&self->future_frame_queue);
+
+ g_assert (len <= self->max_future_frames + 1);
+
+ if (self->to_process) {
+ GST_WARNING_OBJECT (self, "Found uncleared processing buffer");
+ gst_clear_buffer (&self->to_process);
+ }
+
+ if (len > self->max_future_frames ||
+ /* NULL means drain */
+ (buffer == NULL && len > 0)) {
+ GstClockTime cur_timestmap = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+ GstBuffer *next_buf;
+
+ self->to_process =
+ (GstBuffer *) g_queue_pop_head (&self->future_frame_queue);
+
+ /* For non-blend methods, we will produce two frames from a single
+ * interlaced frame. So, sufficiently correct buffer duration is required
+ * to set timestamp for the second output frame */
+ if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND) {
+ if (GST_BUFFER_PTS_IS_VALID (self->to_process)) {
+ cur_timestmap = GST_BUFFER_PTS (self->to_process);
+ } else {
+ cur_timestmap = GST_BUFFER_DTS (self->to_process);
+ }
+
+ /* Ensure buffer duration */
+ next_buf = (GstBuffer *) g_queue_peek_head (&self->future_frame_queue);
+ if (next_buf && GST_CLOCK_STIME_IS_VALID (cur_timestmap)) {
+ GstClockTime next_timestamp;
+
+ if (GST_BUFFER_PTS_IS_VALID (next_buf)) {
+ next_timestamp = GST_BUFFER_PTS (next_buf);
+ } else {
+ next_timestamp = GST_BUFFER_DTS (next_buf);
+ }
+
+ if (GST_CLOCK_STIME_IS_VALID (next_timestamp)) {
+ if (trans->segment.rate >= 0.0 && next_timestamp > cur_timestmap) {
+ duration = next_timestamp - cur_timestmap;
+ } else if (trans->segment.rate < 0.0
+ && next_timestamp < cur_timestmap) {
+ duration = cur_timestmap - next_timestamp;
+ }
+ }
+ }
+
+ /* Make sure that we can update buffer duration safely */
+ self->to_process = gst_buffer_make_writable (self->to_process);
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ GST_BUFFER_DURATION (self->to_process) = duration;
+ } else {
+ GST_BUFFER_DURATION (self->to_process) = self->default_buffer_duration;
+ }
+
+ /* Bonus points, DTS doesn't make sense for raw video frame */
+ GST_BUFFER_PTS (self->to_process) = cur_timestmap;
+ GST_BUFFER_DTS (self->to_process) = GST_CLOCK_TIME_NONE;
+
+ /* And mark the number of output frames for this input frame */
+ self->num_output_per_input = 2;
+ } else {
+ self->num_output_per_input = 1;
+ }
+
+ self->first_output = TRUE;
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_d3d11_deinterlace_submit_input_buffer (GstBaseTransform * trans,
+ gboolean is_discont, GstBuffer * input)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstFlowReturn ret;
+ GstBuffer *buf;
+
+ /* Let baseclass handle QoS first */
+ ret = GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans,
+ is_discont, input);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ if (gst_base_transform_is_passthrough (trans))
+ return ret;
+
+ /* at this moment, baseclass must hold queued_buf */
+ g_assert (trans->queued_buf != NULL);
+
+ /* Check if we can use this buffer directly. If not, copy this into
+ * our fallback buffer */
+ buf = trans->queued_buf;
+ trans->queued_buf = NULL;
+
+ buf = gst_d3d11_deinterlace_ensure_input_buffer (self, buf);
+ if (!buf) {
+ GST_ERROR_OBJECT (self, "Invalid input buffer");
+ return GST_FLOW_ERROR;
+ }
+
+ return gst_d3d11_deinterlace_submit_future_frame (self, buf);
+}
+
+static ID3D11VideoProcessorOutputView *
+gst_d3d11_deinterace_get_pov_from_buffer (GstD3D11Deinterlace * self,
+ GstBuffer * buffer)
+{
+ GstMemory *mem;
+ GstD3D11Memory *dmem;
+ ID3D11VideoProcessorOutputView *pov;
+
+ if (gst_buffer_n_memory (buffer) != 1) {
+ GST_WARNING_OBJECT (self, "Output buffer has more than one memory");
+ return NULL;
+ }
+
+ mem = gst_buffer_peek_memory (buffer, 0);
+ if (!gst_is_d3d11_memory (mem)) {
+ GST_WARNING_OBJECT (self, "Output buffer is holding non-D3D11 memory");
+ return NULL;
+ }
+
+ dmem = (GstD3D11Memory *) mem;
+ if (dmem->device != self->device) {
+ GST_WARNING_OBJECT (self,
+ "Output D3D11 memory was allocated by other device");
+ return NULL;
+ }
+
+ pov = gst_d3d11_memory_get_processor_output_view (dmem,
+ self->video_device, self->video_enum);
+ if (!pov) {
+ GST_WARNING_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
+ return NULL;
+ }
+
+ return pov;
+}
+
+static GstBuffer *
+gst_d3d11_deinterlace_ensure_output_buffer (GstD3D11Deinterlace * self,
+ GstBuffer * output)
+{
+ GstD3D11Memory *dmem;
+ ID3D11VideoProcessorOutputView *pov;
+ GstBuffer *new_buf = NULL;
+
+ pov = gst_d3d11_deinterace_get_pov_from_buffer (self, output);
+ if (pov)
+ return output;
+
+ if (!self->fallback_out_pool ||
+ !gst_buffer_pool_set_active (self->fallback_out_pool, TRUE) ||
+ gst_buffer_pool_acquire_buffer (self->fallback_out_pool, &new_buf,
+ NULL) != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (self, "Fallback output buffer is unavailable");
+ gst_buffer_unref (output);
+
+ return NULL;
+ }
+
+ dmem = (GstD3D11Memory *) gst_buffer_peek_memory (new_buf, 0);
+ pov = gst_d3d11_memory_get_processor_output_view (dmem,
+ self->video_device, self->video_enum);
+ if (!pov) {
+ GST_ERROR_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
+ gst_buffer_unref (new_buf);
+ gst_buffer_unref (output);
+
+ return NULL;
+ }
+
+ /* copy metadata, default implemenation of baseclass will copy everything
+ * what we need */
+ GST_BASE_TRANSFORM_CLASS (parent_class)->copy_metadata
+ (GST_BASE_TRANSFORM_CAST (self), output, new_buf);
+
+ gst_buffer_unref (output);
+
+ return new_buf;
+}
+
+static GstFlowReturn
+gst_d3d11_deinterlace_submit_past_frame (GstD3D11Deinterlace * self,
+ GstBuffer * buffer)
+{
+ /* push head and pop tail, so that head frame can be the nearest frame
+ * of current frame */
+ g_queue_push_head (&self->past_frame_queue, buffer);
+ while (g_queue_get_length (&self->past_frame_queue) > self->max_past_frames) {
+ GstBuffer *to_drop =
+ (GstBuffer *) g_queue_pop_tail (&self->past_frame_queue);
+
+ if (to_drop)
+ gst_buffer_unref (to_drop);
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_d3d11_deinterlace_generate_output (GstBaseTransform * trans,
+ GstBuffer ** outbuf)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *inbuf;
+ GstBuffer *buf = NULL;
+
+ if (gst_base_transform_is_passthrough (trans)) {
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->generate_output (trans,
+ outbuf);
+ }
+
+ *outbuf = NULL;
+ inbuf = self->to_process;
+ if (inbuf == NULL)
+ return GST_FLOW_OK;
+
+ ret =
+ GST_BASE_TRANSFORM_CLASS (parent_class)->prepare_output_buffer (trans,
+ inbuf, &buf);
+
+ if (ret != GST_FLOW_OK || !buf) {
+ GST_WARNING_OBJECT (trans, "could not get buffer from pool: %s",
+ gst_flow_get_name (ret));
+
+ return ret;
+ }
+
+ g_assert (inbuf != buf);
+
+ buf = gst_d3d11_deinterlace_ensure_output_buffer (self, buf);
+ if (!buf) {
+ GST_ERROR_OBJECT (self, "Failed to allocate output buffer to process");
+
+ return GST_FLOW_ERROR;
+ }
+
+ ret = gst_d3d11_deinterlace_transform (trans, inbuf, buf);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref (buf);
+ return ret;
+ }
+
+ g_assert (self->num_output_per_input == 1 || self->num_output_per_input == 2);
+
+ /* Update timestamp and buffer duration.
+ * Here, PTS and duration of inbuf must be valid,
+ * unless there's programing error, since we updated timestamp and duration
+ * already around submit_input_buffer() */
+ if (self->num_output_per_input == 2) {
+ if (!GST_BUFFER_DURATION_IS_VALID (inbuf)) {
+ GST_LOG_OBJECT (self, "Input buffer duration is unknown");
+ } else if (!GST_BUFFER_PTS_IS_VALID (inbuf)) {
+ GST_LOG_OBJECT (self, "Input buffer timestamp is unknown");
+ } else {
+ GstClockTime duration = GST_BUFFER_DURATION (inbuf) / 2;
+ gboolean second_field = FALSE;
+
+ if (self->first_output) {
+ /* For reverse playback, first output is the second field */
+ if (trans->segment.rate < 0)
+ second_field = TRUE;
+ else
+ second_field = FALSE;
+ } else {
+ if (trans->segment.rate < 0)
+ second_field = FALSE;
+ else
+ second_field = TRUE;
+ }
+
+ GST_BUFFER_DURATION (buf) = duration;
+ if (second_field) {
+ GST_BUFFER_PTS (buf) = GST_BUFFER_PTS (buf) + duration;
+ }
+ }
+ }
+
+ *outbuf = buf;
+ self->first_output = FALSE;
+ self->num_transformed++;
+ /* https://docs.microsoft.com/en-us/windows/win32/api/d3d12video/ns-d3d12video-d3d12_video_process_input_stream_rate */
+ if (self->method == GST_D3D11_DEINTERLACE_METHOD_BLEND) {
+ self->input_index += 2;
+ } else {
+ self->input_index++;
+ }
+
+ if (self->num_output_per_input <= self->num_transformed) {
+ /* Move processed frame to past_frame queue */
+ gst_d3d11_deinterlace_submit_past_frame (self, self->to_process);
+ self->to_process = NULL;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_d3d11_deinterlace_transform (GstBaseTransform * trans, GstBuffer * inbuf,
+ GstBuffer * outbuf)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+ ID3D11VideoProcessorInputView *piv;
+ ID3D11VideoProcessorOutputView *pov;
+ D3D11_VIDEO_FRAME_FORMAT frame_foramt = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
+ D3D11_VIDEO_PROCESSOR_STREAM proc_stream = { 0, };
+ ID3D11VideoProcessorInputView *future_surfaces[MAX_NUM_REFERENCES] =
+ { NULL, };
+ ID3D11VideoProcessorInputView *past_surfaces[MAX_NUM_REFERENCES] = { NULL, };
+ guint future_frames = 0;
+ guint past_frames = 0;
+ HRESULT hr;
+ guint i;
+
+ /* Input/output buffer must be holding valid D3D11 memory here,
+ * as we checked it already in submit_input_buffer() and generate_output() */
+ piv = gst_d3d11_deinterace_get_piv_from_buffer (self, inbuf);
+ if (!piv) {
+ GST_ERROR_OBJECT (self, "ID3D11VideoProcessorInputView is unavailable");
+ return GST_FLOW_ERROR;
+ }
+
+ pov = gst_d3d11_deinterace_get_pov_from_buffer (self, outbuf);
+ if (!pov) {
+ GST_ERROR_OBJECT (self, "ID3D11VideoProcessorOutputView is unavailable");
+ return GST_FLOW_ERROR;
+ }
+
+ /* Check field order */
+ if (GST_VIDEO_INFO_INTERLACE_MODE (&self->in_info) ==
+ GST_VIDEO_INTERLACE_MODE_MIXED ||
+ (GST_VIDEO_INFO_INTERLACE_MODE (&self->in_info) ==
+ GST_VIDEO_INTERLACE_MODE_INTERLEAVED &&
+ GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
+ GST_VIDEO_FIELD_ORDER_UNKNOWN)) {
+ if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
+ frame_foramt = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
+ } else if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_VIDEO_BUFFER_FLAG_TFF)) {
+ frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
+ } else {
+ frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
+ }
+ } else if (GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
+ GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
+ frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
+ } else if (GST_VIDEO_INFO_FIELD_ORDER (&self->in_info) ==
+ GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST) {
+ frame_foramt = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
+ }
+
+ if (frame_foramt == D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE) {
+ /* Progressive stream will produce only one frame per frame */
+ self->num_output_per_input = 1;
+ } else if (self->method != GST_D3D11_DEINTERLACE_METHOD_BLEND &&
+ self->method != GST_D3D11_DEINTERLACE_METHOD_BOB) {
+ /* Fill reference frames */
+ for (i = 0; i < g_queue_get_length (&self->future_frame_queue) &&
+ i < G_N_ELEMENTS (future_surfaces); i++) {
+ GstBuffer *future_buf;
+ ID3D11VideoProcessorInputView *future_piv;
+
+ future_buf =
+ (GstBuffer *) g_queue_peek_nth (&self->future_frame_queue, i);
+ future_piv = gst_d3d11_deinterace_get_piv_from_buffer (self, future_buf);
+ if (!future_piv) {
+ GST_WARNING_OBJECT (self,
+ "Couldn't get ID3D11VideoProcessorInputView from future "
+ "reference %d", i);
+ break;
+ }
+
+ future_surfaces[i] = future_piv;
+ future_frames++;
+ }
+
+ for (i = 0; i < g_queue_get_length (&self->past_frame_queue) &&
+ i < G_N_ELEMENTS (past_surfaces); i++) {
+ GstBuffer *past_buf;
+ ID3D11VideoProcessorInputView *past_piv;
+
+ past_buf = (GstBuffer *) g_queue_peek_nth (&self->past_frame_queue, i);
+ past_piv = gst_d3d11_deinterace_get_piv_from_buffer (self, past_buf);
+ if (!past_piv) {
+ GST_WARNING_OBJECT (self,
+ "Couldn't get ID3D11VideoProcessorInputView from past "
+ "reference %d", i);
+ break;
+ }
+
+ past_surfaces[i] = past_piv;
+ past_frames++;
+ }
+ }
+
+ proc_stream.Enable = TRUE;
+ proc_stream.pInputSurface = piv;
+ proc_stream.InputFrameOrField = self->input_index;
+ /* FIXME: This is wrong for inverse telechin case */
+ /* OutputIndex == 0 for the first field, and 1 for the second field */
+ if (self->num_output_per_input == 2) {
+ if (trans->segment.rate < 0.0) {
+ /* Process the second frame first in case of reverse playback */
+ proc_stream.OutputIndex = self->first_output ? 1 : 0;
+ } else {
+ proc_stream.OutputIndex = self->first_output ? 0 : 1;
+ }
+ } else {
+ proc_stream.OutputIndex = 0;
+ }
+
+ if (future_frames) {
+ proc_stream.FutureFrames = future_frames;
+ proc_stream.ppFutureSurfaces = future_surfaces;
+ }
+
+ if (past_frames) {
+ proc_stream.PastFrames = past_frames;
+ proc_stream.ppPastSurfaces = past_surfaces;
+ }
+
+ gst_d3d11_device_lock (self->device);
+ self->video_context->VideoProcessorSetStreamFrameFormat (self->video_proc, 0,
+ frame_foramt);
+
+ hr = self->video_context->VideoProcessorBlt (self->video_proc, pov, 0,
+ 1, &proc_stream);
+ gst_d3d11_device_unlock (self->device);
+
+ if (!gst_d3d11_result (hr, self->device)) {
+ GST_ERROR_OBJECT (self, "Failed to perform deinterlacing");
+ return GST_FLOW_ERROR;
+ }
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_d3d11_deinterlace_sink_event (GstBaseTransform * trans, GstEvent * event)
+{
+ GstD3D11Deinterlace *self = GST_D3D11_DEINTERLACE (trans);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_STREAM_START:
+ /* stream-start means discont stream from previous one. Drain pending
+ * frame if any */
+ GST_DEBUG_OBJECT (self, "Have stream-start, drain frames if any");
+ gst_d3d11_deinterlace_drain (self);
+ break;
+ case GST_EVENT_CAPS:{
+ GstPad *sinkpad = GST_BASE_TRANSFORM_SINK_PAD (trans);
+ GstCaps *prev_caps;
+
+ prev_caps = gst_pad_get_current_caps (sinkpad);
+ if (prev_caps) {
+ GstCaps *caps;
+ gst_event_parse_caps (event, &caps);
+ /* If caps is updated, drain pending frames */
+ if (!gst_caps_is_equal (prev_caps, caps)) {
+ GST_DEBUG_OBJECT (self, "Caps updated from %" GST_PTR_FORMAT " to %"
+ GST_PTR_FORMAT, prev_caps, caps);
+ gst_d3d11_deinterlace_drain (self);
+ }
+
+ gst_caps_unref (prev_caps);
+ }
+ break;
+ }
+ case GST_EVENT_SEGMENT:
+ /* new segment would mean that temporal discontinuity */
+ case GST_EVENT_SEGMENT_DONE:
+ case GST_EVENT_EOS:
+ GST_DEBUG_OBJECT (self, "Have event %s, drain frames if any",
+ GST_EVENT_TYPE_NAME (event));
+ gst_d3d11_deinterlace_drain (self);
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ GST_D3D11_DEINTERLACE_LOCK (self);
+ gst_d3d11_deinterlace_reset_history (self);
+ GST_D3D11_DEINTERLACE_UNLOCK (self);
+ break;
+ default:
+ break;
+ }
+
+ return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
+}
+
+/* FIXME: might be job of basetransform */
+static GstFlowReturn
+gst_d3d11_deinterlace_drain (GstD3D11Deinterlace * self)
+{
+ GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (self);
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *outbuf = NULL;
+
+ GST_D3D11_DEINTERLACE_LOCK (self);
+ if (gst_base_transform_is_passthrough (trans)) {
+ /* If we were passthrough, nothing to do */
+ goto done;
+ } else if (!g_queue_get_length (&self->future_frame_queue)) {
+ /* No pending data, nothing to do */
+ goto done;
+ }
+
+ while (g_queue_get_length (&self->future_frame_queue)) {
+ gst_d3d11_deinterlace_submit_future_frame (self, NULL);
+ if (!self->to_process)
+ break;
+
+ do {
+ outbuf = NULL;
+
+ ret = gst_d3d11_deinterlace_generate_output (trans, &outbuf);
+ if (outbuf != NULL) {
+ /* Release lock during push buffer */
+ GST_D3D11_DEINTERLACE_UNLOCK (self);
+ ret = gst_pad_push (trans->srcpad, outbuf);
+ GST_D3D11_DEINTERLACE_LOCK (self);
+ }
+ } while (ret == GST_FLOW_OK && outbuf != NULL);
+ }
+
+done:
+ gst_d3d11_deinterlace_reset_history (self);
+ GST_D3D11_DEINTERLACE_UNLOCK (self);
+
+ return ret;
+}
+
+/* GstD3D11DeinterlaceBin */
+enum
+{
+ PROP_BIN_0,
+ /* basetransform */
+ PROP_BIN_QOS,
+ /* deinterlace */
+ PROP_BIN_ADAPTER,
+ PROP_BIN_DEVICE_ID,
+ PROP_BIN_VENDOR_ID,
+ PROP_BIN_METHOD,
+ PROP_BIN_SUPPORTED_METHODS,
+};
+
+typedef struct _GstD3D11DeinterlaceBin
+{
+ GstBin parent;
+
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ GstElement *deinterlace;
+ GstElement *in_convert;
+ GstElement *out_convert;
+ GstElement *upload;
+ GstElement *download;
+} GstD3D11DeinterlaceBin;
+
+typedef struct _GstD3D11DeinterlaceBinClass
+{
+ GstBinClass parent_class;
+
+ guint adapter;
+ GType child_type;
+} GstD3D11DeinterlaceBinClass;
+
+static GstElementClass *bin_parent_class = NULL;
+#define GST_D3D11_DEINTERLACE_BIN(object) ((GstD3D11DeinterlaceBin *) (object))
+#define GST_D3D11_DEINTERLACE_BIN_GET_CLASS(object) \
+ (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object), \
+ GstD3D11DeinterlaceBinClass))
+#define GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE(features,format) \
+ "video/x-raw(" features "), " \
+ "format = (string) " format ", " \
+ "width = (int) [64, 8192], " \
+ "height = (int) [64, 8192] "
+
+static GstStaticPadTemplate bin_sink_template_caps =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE
+ (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SINK_FORMATS) "; "
+ GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE
+ (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
+ GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
+ GST_D3D11_SINK_FORMATS)));
+
+static GstStaticPadTemplate bin_src_template_caps =
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE
+ (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SRC_FORMATS) "; "
+ GST_D3D11_DEINTERLACE_BIN_CAPS_MAKE
+ (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY ","
+ GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
+ GST_D3D11_SRC_FORMATS)));
+
+static void gst_d3d11_deinterlace_bin_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_d3d11_deinterlace_bin_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+static void
+gst_d3d11_deinterlace_bin_class_init (GstD3D11DeinterlaceBinClass * klass,
+ gpointer data)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstD3D11DeinterlaceClassData *cdata = (GstD3D11DeinterlaceClassData *) data;
+ gchar *long_name;
+
+ bin_parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
+
+ gobject_class->get_property = gst_d3d11_deinterlace_bin_get_property;
+ gobject_class->set_property = gst_d3d11_deinterlace_bin_set_property;
+
+ /* basetransform */
+ g_object_class_install_property (gobject_class, PROP_BIN_QOS,
+ g_param_spec_boolean ("qos", "QoS", "Handle Quality-of-Service events",
+ FALSE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ /* deinterlace */
+ g_object_class_install_property (gobject_class, PROP_BIN_ADAPTER,
+ g_param_spec_uint ("adapter", "Adapter",
+ "DXGI Adapter index for creating device",
+ 0, G_MAXUINT32, cdata->adapter,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_BIN_DEVICE_ID,
+ g_param_spec_uint ("device-id", "Device Id",
+ "DXGI Device ID", 0, G_MAXUINT32, 0,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_BIN_VENDOR_ID,
+ g_param_spec_uint ("vendor-id", "Vendor Id",
+ "DXGI Vendor ID", 0, G_MAXUINT32, 0,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_BIN_METHOD,
+ g_param_spec_flags ("method", "Method",
+ "Deinterlace Method. Use can set multiple methods as a flagset "
+ "and element will select one of method automatically. "
+ "If deinterlacing device failed to deinterlace with given mode, "
+ "fallback might happen by the device",
+ GST_TYPE_D3D11_DEINTERLACE_METHOD, cdata->device_caps.default_method,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_READY)));
+ g_object_class_install_property (gobject_class, PROP_BIN_SUPPORTED_METHODS,
+ g_param_spec_flags ("supported-methods", "Supported Methods",
+ "Set of supported deinterlace methods by device",
+ GST_TYPE_D3D11_DEINTERLACE_METHOD,
+ cdata->device_caps.supported_methods,
+ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ long_name = g_strdup_printf ("Direct3D11 %s Deinterlacer Bin",
+ cdata->description);
+ gst_element_class_set_metadata (element_class, long_name,
+ "Filter/Effect/Video/Deinterlace/Hardware",
+ "A Direct3D11 based deinterlacer",
+ "Seungha Yang <seungha@centricular.com>");
+ g_free (long_name);
+
+ gst_element_class_add_static_pad_template (element_class,
+ &bin_sink_template_caps);
+ gst_element_class_add_static_pad_template (element_class,
+ &bin_src_template_caps);
+
+ klass->adapter = cdata->adapter;
+ klass->child_type = cdata->deinterlace_type;
+
+ gst_d3d11_deinterlace_class_data_unref (cdata);
+}
+
+static void
+gst_d3d11_deinterlace_bin_init (GstD3D11DeinterlaceBin * self)
+{
+ GstD3D11DeinterlaceBinClass *klass =
+ GST_D3D11_DEINTERLACE_BIN_GET_CLASS (self);
+ GstPad *pad;
+
+ self->deinterlace = (GstElement *) g_object_new (klass->child_type,
+ "name", "deinterlace", NULL);
+ self->in_convert = gst_element_factory_make ("d3d11colorconvert", NULL);
+ self->out_convert = gst_element_factory_make ("d3d11colorconvert", NULL);
+ self->upload = gst_element_factory_make ("d3d11upload", NULL);
+ self->download = gst_element_factory_make ("d3d11download", NULL);
+
+ /* Specify DXGI adapter index to use */
+ g_object_set (G_OBJECT (self->in_convert), "adapter", klass->adapter, NULL);
+ g_object_set (G_OBJECT (self->out_convert), "adapter", klass->adapter, NULL);
+ g_object_set (G_OBJECT (self->upload), "adapter", klass->adapter, NULL);
+ g_object_set (G_OBJECT (self->download), "adapter", klass->adapter, NULL);
+
+ gst_bin_add_many (GST_BIN_CAST (self), self->upload, self->in_convert,
+ self->deinterlace, self->out_convert, self->download, NULL);
+ gst_element_link_many (self->upload, self->in_convert, self->deinterlace,
+ self->out_convert, self->download, NULL);
+
+ pad = gst_element_get_static_pad (self->upload, "sink");
+ self->sinkpad = gst_ghost_pad_new ("sink", pad);
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
+ gst_object_unref (pad);
+
+ pad = gst_element_get_static_pad (self->download, "src");
+ self->srcpad = gst_ghost_pad_new ("src", pad);
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
+ gst_object_unref (pad);
+}
+
+static void
+gst_d3d11_deinterlace_bin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstD3D11DeinterlaceBin *self = GST_D3D11_DEINTERLACE_BIN (object);
+
+ g_object_set_property (G_OBJECT (self->deinterlace), pspec->name, value);
+}
+
+static void
+gst_d3d11_deinterlace_bin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstD3D11DeinterlaceBin *self = GST_D3D11_DEINTERLACE_BIN (object);
+
+ g_object_get_property (G_OBJECT (self->deinterlace), pspec->name, value);
+}
+
+void
+gst_d3d11_deinterlace_register (GstPlugin * plugin, GstD3D11Device * device,
+ guint rank)
+{
+ GType type;
+ GType bin_type;
+ gchar *type_name;
+ gchar *feature_name;
+ guint index = 0;
+ GTypeInfo type_info = {
+ sizeof (GstD3D11DeinterlaceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_d3d11_deinterlace_class_init,
+ NULL,
+ NULL,
+ sizeof (GstD3D11Deinterlace),
+ 0,
+ (GInstanceInitFunc) gst_d3d11_deinterlace_init,
+ };
+ GTypeInfo bin_type_info = {
+ sizeof (GstD3D11DeinterlaceBinClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_d3d11_deinterlace_bin_class_init,
+ NULL,
+ NULL,
+ sizeof (GstD3D11DeinterlaceBin),
+ 0,
+ (GInstanceInitFunc) gst_d3d11_deinterlace_bin_init,
+ };
+ GstCaps *sink_caps = NULL;
+ GstCaps *src_caps = NULL;
+ GstCaps *caps = NULL;
+ GstCapsFeatures *caps_features;
+ ID3D11Device *device_handle;
+ ID3D11DeviceContext *context_handle;
+ /* *INDENT-OFF* */
+ ComPtr<ID3D11VideoDevice> video_device;
+ ComPtr<ID3D11VideoContext> video_context;
+ ComPtr<ID3D11VideoProcessorEnumerator> video_proc_enum;
+ ComPtr<ID3D11VideoProcessorEnumerator1> video_proc_enum1;
+ /* *INDENT-ON* */
+ HRESULT hr;
+ D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc;
+ D3D11_VIDEO_PROCESSOR_CAPS proc_caps = { 0, };
+ UINT supported_methods = 0;
+ GstD3D11DeinterlaceMethod default_method;
+ gboolean blend;
+ gboolean bob;
+ gboolean adaptive;
+ gboolean mocomp;
+ /* NOTE: processor might be able to handle other formats.
+ * However, not all YUV formats can be used for render target.
+ * For instance, DXGI_FORMAT_Y210 and DXGI_FORMAT_Y410 formats cannot be
+ * render target. In practice, interlaced stream would output of video
+ * decoders, so NV12/P010/P016 can cover most of real-world use case.
+ */
+ DXGI_FORMAT formats_to_check[] = {
+ DXGI_FORMAT_NV12, /* NV12 */
+ DXGI_FORMAT_P010, /* P010_10LE */
+ DXGI_FORMAT_P016, /* P016_LE */
+ };
+ GValue *supported_formats = NULL;
+ GstD3D11DeinterlaceClassData *cdata;
+ guint max_past_frames = 0;
+ guint max_future_frames = 0;
+ guint i;
+
+ device_handle = gst_d3d11_device_get_device_handle (device);
+ context_handle = gst_d3d11_device_get_device_context_handle (device);
+
+ hr = device_handle->QueryInterface (IID_PPV_ARGS (&video_device));
+ if (!gst_d3d11_result (hr, device))
+ return;
+
+ hr = context_handle->QueryInterface (IID_PPV_ARGS (&video_context));
+ if (!gst_d3d11_result (hr, device))
+ return;
+
+ memset (&desc, 0, sizeof (D3D11_VIDEO_PROCESSOR_CONTENT_DESC));
+ desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
+ desc.InputWidth = 320;
+ desc.InputHeight = 240;
+ desc.OutputWidth = 320;
+ desc.OutputHeight = 240;
+ desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
+
+ hr = video_device->CreateVideoProcessorEnumerator (&desc, &video_proc_enum);
+ if (!gst_d3d11_result (hr, device))
+ return;
+
+ /* We need ID3D11VideoProcessorEnumerator1 interface to check conversion
+ * capability of device via CheckVideoProcessorFormatConversion() */
+ hr = video_proc_enum.As (&video_proc_enum1);
+ if (!gst_d3d11_result (hr, device))
+ return;
+
+ hr = video_proc_enum->GetVideoProcessorCaps (&proc_caps);
+ if (!gst_d3d11_result (hr, device))
+ return;
+
+ for (i = 0; i < proc_caps.RateConversionCapsCount; i++) {
+ D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rate_conv_caps = { 0, };
+
+ hr = video_proc_enum->GetVideoProcessorRateConversionCaps (i,
+ &rate_conv_caps);
+ if (FAILED (hr))
+ continue;
+
+ supported_methods |= rate_conv_caps.ProcessorCaps;
+ max_past_frames = MAX (max_past_frames, rate_conv_caps.PastFrames);
+ max_future_frames = MAX (max_future_frames, rate_conv_caps.FutureFrames);
+ }
+
+ if (supported_methods == 0)
+ return;
+
+#define IS_SUPPORTED_METHOD(flags,val) (flags & val) == val
+ blend = IS_SUPPORTED_METHOD (supported_methods,
+ GST_D3D11_DEINTERLACE_METHOD_BLEND);
+ bob = IS_SUPPORTED_METHOD (supported_methods,
+ GST_D3D11_DEINTERLACE_METHOD_BOB);
+ adaptive = IS_SUPPORTED_METHOD (supported_methods,
+ GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE);
+ mocomp = IS_SUPPORTED_METHOD (supported_methods,
+ GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION);
+#undef IS_SUPPORTED_METHOD
+
+ if (!blend && !bob && !adaptive && !mocomp)
+ return;
+
+ /* Drop all not supported methods from flags */
+ supported_methods = supported_methods &
+ (GST_D3D11_DEINTERLACE_METHOD_BLEND | GST_D3D11_DEINTERLACE_METHOD_BOB |
+ GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE |
+ GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION);
+
+ /* Prefer bob, it's equivalent to "linear" which is default mode of
+ * software deinterlace element, also it's fallback mode
+ * for our "adaptive" and "mocomp" modes. Note that since Direct3D12, "blend"
+ * mode is no more supported, instead "bob" and "custom" mode are suported
+ * by Direct3D12 */
+ if (bob) {
+ default_method = GST_D3D11_DEINTERLACE_METHOD_BOB;
+ } else if (adaptive) {
+ default_method = GST_D3D11_DEINTERLACE_METHOD_ADAPTVIE;
+ } else if (mocomp) {
+ default_method = GST_D3D11_DEINTERLACE_METHOD_MOTION_COMPENSATION;
+ } else if (blend) {
+ default_method = GST_D3D11_DEINTERLACE_METHOD_BLEND;
+ } else {
+ /* Programming error */
+ g_return_if_reached ();
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (formats_to_check); i++) {
+ UINT flags = 0;
+ GValue val = G_VALUE_INIT;
+ GstVideoFormat format;
+ BOOL supported = FALSE;
+
+ hr = video_proc_enum->CheckVideoProcessorFormat (formats_to_check[i],
+ &flags);
+ if (FAILED (hr))
+ continue;
+
+ /* D3D11 video processor can support other conversion at once,
+ * including color format conversion.
+ * But not all combinations of in/out pairs can be supported.
+ * To make things simple, this element will do only deinterlacing
+ * (might not be optimal in terms of processing power/resource though) */
+
+ /* D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT = 0x1,
+ * D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT = 0x2,
+ * MinGW header might not be defining the above enum values */
+ if ((flags & 0x3) != 0x3)
+ continue;
+
+ format = gst_d3d11_dxgi_format_to_gst (formats_to_check[i]);
+ /* This is programming error! */
+ if (format == GST_VIDEO_FORMAT_UNKNOWN) {
+ GST_ERROR ("Couldn't convert DXGI format %d to video format",
+ formats_to_check[i]);
+ continue;
+ }
+
+ hr = video_proc_enum1->CheckVideoProcessorFormatConversion
+ (formats_to_check[i], DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
+ formats_to_check[i], DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
+ &supported);
+ if (FAILED (hr) || !supported)
+ continue;
+
+ if (!supported_formats) {
+ supported_formats = g_new0 (GValue, 1);
+ g_value_init (supported_formats, GST_TYPE_LIST);
+ }
+
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_static_string (&val, gst_video_format_to_string (format));
+ gst_value_list_append_and_take_value (supported_formats, &val);
+ }
+
+ if (!supported_formats)
+ return;
+
+ caps = gst_caps_new_empty_simple ("video/x-raw");
+ /* FIXME: Check supported resolution, it would be different from
+ * supported max texture dimension */
+ gst_caps_set_simple (caps,
+ "width", GST_TYPE_INT_RANGE, 64, 8192,
+ "height", GST_TYPE_INT_RANGE, 64, 8192, NULL);
+ gst_caps_set_value (caps, "format", supported_formats);
+ g_value_unset (supported_formats);
+ g_free (supported_formats);
+
+ /* TODO: Add alternating deinterlace */
+ src_caps = gst_caps_copy (caps);
+ caps_features = gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
+ NULL);
+ gst_caps_set_features_simple (src_caps, caps_features);
+
+ caps_features = gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
+ GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, NULL);
+ gst_caps_set_features_simple (caps, caps_features);
+ gst_caps_append (src_caps, caps);
+
+ sink_caps = gst_caps_copy (src_caps);
+
+ GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+ GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
+
+ cdata = gst_d3d11_deinterlace_class_data_new ();
+ cdata->sink_caps = sink_caps;
+ cdata->src_caps = src_caps;
+ cdata->device_caps.supported_methods =
+ (GstD3D11DeinterlaceMethod) supported_methods;
+ cdata->device_caps.default_method = default_method;
+ cdata->device_caps.max_past_frames = max_past_frames;
+ cdata->device_caps.max_future_frames = max_future_frames;
+
+ g_object_get (device, "adapter", &cdata->adapter,
+ "device-id", &cdata->device_id, "vendor-id", &cdata->vendor_id,
+ "description", &cdata->description, NULL);
+ type_info.class_data = cdata;
+ bin_type_info.class_data = gst_d3d11_deinterlace_class_data_ref (cdata);
+
+ type_name = g_strdup ("GstD3D11Deinterlace");
+ feature_name = g_strdup ("d3d11deinterlaceelement");
+
+ while (g_type_from_name (type_name)) {
+ index++;
+ g_free (type_name);
+ g_free (feature_name);
+ type_name = g_strdup_printf ("GstD3D11Device%dDeinterlace", index);
+ feature_name = g_strdup_printf ("d3d11device%ddeinterlaceelement", index);
+ }
+
+ type = g_type_register_static (GST_TYPE_BASE_TRANSFORM,
+ type_name, &type_info, (GTypeFlags) 0);
+ cdata->deinterlace_type = type;
+
+ if (!gst_element_register (plugin, feature_name, GST_RANK_NONE, type))
+ GST_WARNING ("Failed to register plugin '%s'", type_name);
+
+ g_free (type_name);
+ g_free (feature_name);
+
+ /* Register wrapper bin */
+ index = 0;
+ type_name = g_strdup ("GstD3D11DeinterlaceBin");
+ feature_name = g_strdup ("d3d11deinterlace");
+
+ while (g_type_from_name (type_name)) {
+ index++;
+ g_free (type_name);
+ g_free (feature_name);
+ type_name = g_strdup_printf ("GstD3D11Device%dDeinterlaceBin", index);
+ feature_name = g_strdup_printf ("d3d11device%ddeinterlace", index);
+ }
+
+ bin_type = g_type_register_static (GST_TYPE_BIN,
+ type_name, &bin_type_info, (GTypeFlags) 0);
+
+ /* make lower rank than default device */
+ if (rank > 0 && index != 0)
+ rank--;
+
+ if (!gst_element_register (plugin, feature_name, rank, bin_type))
+ GST_WARNING ("Failed to register plugin '%s'", type_name);
+
+ g_free (type_name);
+ g_free (feature_name);
+}
diff --git a/sys/d3d11/gstd3d11deinterlace.h b/sys/d3d11/gstd3d11deinterlace.h
new file mode 100644
index 000000000..53477cce9
--- /dev/null
+++ b/sys/d3d11/gstd3d11deinterlace.h
@@ -0,0 +1,34 @@
+/* GStreamer
+ * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_D3D11_DEINTERLACE_H__
+#define __GST_D3D11_DEINTERLACE_H__
+
+#include <gst/gst.h>
+#include <gst/d3d11/gstd3d11.h>
+
+G_BEGIN_DECLS
+
+void gst_d3d11_deinterlace_register (GstPlugin * plugin,
+ GstD3D11Device * device,
+ guint rank);
+
+G_END_DECLS
+
+#endif /* __GST_D3D11_DEINTERLACE_H__ */
diff --git a/sys/d3d11/meson.build b/sys/d3d11/meson.build
index dc6f2d810..8a3a3b3c8 100644
--- a/sys/d3d11/meson.build
+++ b/sys/d3d11/meson.build
@@ -86,6 +86,12 @@ if d3d11_winapi_desktop
endif
endif
+# need dxgi1_5.h for HDR10 processing and d3d11_4.h for ID3D11VideoContext2 interface
+if d3d11_conf.get('GST_D3D11_DXGI_HEADER_VERSION') >= 5 and d3d11_conf.get('GST_D3D11_HEADER_VERSION') >= 4
+ d3d11_sources += ['gstd3d11deinterlace.cpp']
+ extra_c_args += ['-DHAVE_D3D11_VIDEO_PROC']
+endif
+
# MinGW 32bits compiler seems to be complaining about redundant-decls
# when ComPtr is in use. Let's just disable the warning
if cc.get_id() != 'msvc'
diff --git a/sys/d3d11/plugin.c b/sys/d3d11/plugin.c
index dee560b72..eaaaf6fe8 100644
--- a/sys/d3d11/plugin.c
+++ b/sys/d3d11/plugin.c
@@ -41,6 +41,9 @@
#ifdef HAVE_DXGI_DESKTOP_DUP
#include "gstd3d11desktopdupsrc.h"
#endif
+#ifdef HAVE_D3D11_VIDEO_PROC
+#include "gstd3d11deinterlace.h"
+#endif
GST_DEBUG_CATEGORY (gst_d3d11_debug);
GST_DEBUG_CATEGORY (gst_d3d11_shader_debug);
@@ -65,6 +68,10 @@ GST_DEBUG_CATEGORY (gst_d3d11_mpeg2_dec_debug);
GST_DEBUG_CATEGORY (gst_d3d11_desktop_dup_debug);
#endif
+#ifdef HAVE_D3D11_VIDEO_PROC
+GST_DEBUG_CATEGORY (gst_d3d11_deinterlace_debug);
+#endif
+
#define GST_CAT_DEFAULT gst_d3d11_debug
static gboolean
@@ -116,6 +123,11 @@ plugin_init (GstPlugin * plugin)
}
#endif
+#ifdef HAVE_D3D11_VIDEO_PROC
+ GST_DEBUG_CATEGORY_INIT (gst_d3d11_deinterlace_debug,
+ "d3d11deinterlace", 0, "Direct3D11 Deinterlacer");
+#endif
+
/* Enumerate devices to register decoders per device and to get the highest
* feature level */
/* AMD seems supporting up to 12 cards, and 8 for NVIDIA */
@@ -169,6 +181,17 @@ plugin_init (GstPlugin * plugin)
}
#endif
+#ifdef HAVE_D3D11_VIDEO_PROC
+ /* D3D11 video processor API is availble since Windows 8 */
+ if (gst_d3d11_is_windows_8_or_greater ()) {
+ gboolean hardware;
+
+ g_object_get (device, "hardware", &hardware, NULL);
+ if (hardware)
+ gst_d3d11_deinterlace_register (plugin, device, GST_RANK_MARGINAL);
+ }
+#endif
+
gst_object_unref (device);
}