d777aaaf7c8b847d5a495c1f3ea67d92b0f78aac
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / d3d11 / gstd3d11compositor.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2004, 2008 Wim Taymans <wim@fluendo.com>
4  * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
6  * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
7  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 /**
26  * SECTION:element-d3d11compositor
27  * @title: d3d11compositor
28  *
29  * A Direct3D11 based video compositing element.
30  *
31  * ## Example launch line
32  * ```
33  * gst-launch-1.0 d3d11compositor name=c ! d3d11videosink \
34  *     videotestsrc ! video/x-raw,width=320,height=240 ! c. \
35  *     videotestsrc pattern=ball ! video/x-raw,width=100,height=100 ! c.
36  *
37  * Since: 1.20
38  *
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include "gstd3d11compositor.h"
46 #include "gstd3d11pluginutils.h"
47 #include <string.h>
48 #include <wrl.h>
49
50 GST_DEBUG_CATEGORY_STATIC (gst_d3d11_compositor_debug);
51 #define GST_CAT_DEFAULT gst_d3d11_compositor_debug
52
53 /* *INDENT-OFF* */
54 using namespace Microsoft::WRL;
55 /* *INDENT-ON* */
56
57 typedef enum
58 {
59   GST_D3D11_COMPOSITOR_BACKGROUND_CHECKER,
60   GST_D3D11_COMPOSITOR_BACKGROUND_BLACK,
61   GST_D3D11_COMPOSITOR_BACKGROUND_WHITE,
62   GST_D3D11_COMPOSITOR_BACKGROUND_TRANSPARENT,
63 } GstD3D11CompositorBackground;
64
65 #define GST_TYPE_D3D11_COMPOSITOR_BACKGROUND (gst_d3d11_compositor_background_get_type())
66 static GType
67 gst_d3d11_compositor_background_get_type (void)
68 {
69   static GType compositor_background_type = 0;
70
71   static const GEnumValue compositor_background[] = {
72     {GST_D3D11_COMPOSITOR_BACKGROUND_CHECKER, "Checker pattern", "checker"},
73     {GST_D3D11_COMPOSITOR_BACKGROUND_BLACK, "Black", "black"},
74     {GST_D3D11_COMPOSITOR_BACKGROUND_WHITE, "White", "white"},
75     {GST_D3D11_COMPOSITOR_BACKGROUND_TRANSPARENT,
76         "Transparent Background to enable further compositing", "transparent"},
77     {0, nullptr, nullptr},
78   };
79
80   if (!compositor_background_type) {
81     compositor_background_type =
82         g_enum_register_static ("GstD3D11CompositorBackground",
83         compositor_background);
84   }
85   return compositor_background_type;
86 }
87
88 typedef enum
89 {
90   GST_D3D11_COMPOSITOR_OPERATOR_SOURCE,
91   GST_D3D11_COMPOSITOR_OPERATOR_OVER,
92 } GstD3D11CompositorOperator;
93
94 /**
95  * GstD3D11CompositorOperator:
96  *
97  * Since: 1.22
98  */
99 #define GST_TYPE_D3D11_COMPOSITOR_OPERATOR (gst_d3d11_compositor_operator_get_type())
100 static GType
101 gst_d3d11_compositor_operator_get_type (void)
102 {
103   static GType compositor_operator_type = 0;
104
105   static const GEnumValue compositor_operator[] = {
106     {GST_D3D11_COMPOSITOR_OPERATOR_SOURCE, "Source", "source"},
107     {GST_D3D11_COMPOSITOR_OPERATOR_OVER, "Over", "over"},
108     {0, nullptr, nullptr},
109   };
110
111   if (!compositor_operator_type) {
112     compositor_operator_type =
113         g_enum_register_static ("GstD3D11CompositorOperator",
114         compositor_operator);
115   }
116   return compositor_operator_type;
117 }
118
119 typedef enum
120 {
121   GST_D3D11_COMPOSITOR_SIZING_POLICY_NONE,
122   GST_D3D11_COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO,
123 } GstD3D11CompositorSizingPolicy;
124
125 #define GST_TYPE_D3D11_COMPOSITOR_SIZING_POLICY (gst_d3d11_compositor_sizing_policy_get_type())
126 static GType
127 gst_d3d11_compositor_sizing_policy_get_type (void)
128 {
129   static GType sizing_policy_type = 0;
130
131   static const GEnumValue sizing_polices[] = {
132     {GST_D3D11_COMPOSITOR_SIZING_POLICY_NONE,
133         "None: Image is scaled to fill configured destination rectangle without "
134           "padding or keeping the aspect ratio", "none"},
135     {GST_D3D11_COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO,
136           "Keep Aspect Ratio: Image is scaled to fit destination rectangle "
137           "specified by GstCompositorPad:{xpos, ypos, width, height} "
138           "with preserved aspect ratio. Resulting image will be centered in "
139           "the destination rectangle with padding if necessary",
140         "keep-aspect-ratio"},
141     {0, nullptr, nullptr},
142   };
143
144   if (!sizing_policy_type) {
145     sizing_policy_type =
146         g_enum_register_static ("GstD3D11CompositorSizingPolicy",
147         sizing_polices);
148   }
149   return sizing_policy_type;
150 }
151
152 /* *INDENT-OFF* */
153 static const gchar checker_vs_src[] =
154     "struct VS_INPUT\n"
155     "{\n"
156     "  float4 Position : POSITION;\n"
157     "};\n"
158     "\n"
159     "struct VS_OUTPUT\n"
160     "{\n"
161     "  float4 Position: SV_POSITION;\n"
162     "};\n"
163     "\n"
164     "VS_OUTPUT main(VS_INPUT input)\n"
165     "{\n"
166     "  return input;\n"
167     "}\n";
168
169 static const gchar checker_ps_src_rgb[] =
170     "static const float blocksize = 8.0;\n"
171     "static const float4 high = float4(0.667, 0.667, 0.667, 1.0);\n"
172     "static const float4 low = float4(0.333, 0.333, 0.333, 1.0);\n"
173     "struct PS_INPUT\n"
174     "{\n"
175     "  float4 Position: SV_POSITION;\n"
176     "};\n"
177     "struct PS_OUTPUT\n"
178     "{\n"
179     "  float4 Plane: SV_TARGET;\n"
180     "};\n"
181     "PS_OUTPUT main(PS_INPUT input)\n"
182     "{\n"
183     "  PS_OUTPUT output;\n"
184     "  if ((input.Position.x % (blocksize * 2.0)) >= blocksize) {\n"
185     "    if ((input.Position.y % (blocksize * 2.0)) >= blocksize)\n"
186     "      output.Plane = low;\n"
187     "    else\n"
188     "      output.Plane = high;\n"
189     "  } else {\n"
190     "    if ((input.Position.y % (blocksize * 2.0)) < blocksize)\n"
191     "      output.Plane = low;\n"
192     "    else\n"
193     "      output.Plane = high;\n"
194     "  }\n"
195     "  return output;\n"
196     "}\n";
197
198 static const gchar checker_ps_src_vuya[] =
199     "static const float blocksize = 8.0;\n"
200     "static const float4 high = float4(0.5, 0.5, 0.667, 1.0);\n"
201     "static const float4 low = float4(0.5, 0.5, 0.333, 1.0);\n"
202     "struct PS_INPUT\n"
203     "{\n"
204     "  float4 Position: SV_POSITION;\n"
205     "};\n"
206     "struct PS_OUTPUT\n"
207     "{\n"
208     "  float4 Plane: SV_TARGET;\n"
209     "};\n"
210     "PS_OUTPUT main(PS_INPUT input)\n"
211     "{\n"
212     "  PS_OUTPUT output;\n"
213     "  if ((input.Position.x % (blocksize * 2.0)) >= blocksize) {\n"
214     "    if ((input.Position.y % (blocksize * 2.0)) >= blocksize)\n"
215     "      output.Plane = low;\n"
216     "    else\n"
217     "      output.Plane = high;\n"
218     "  } else {\n"
219     "    if ((input.Position.y % (blocksize * 2.0)) < blocksize)\n"
220     "      output.Plane = low;\n"
221     "    else\n"
222     "      output.Plane = high;\n"
223     "  }\n"
224     "  return output;\n"
225     "}\n";
226
227 static const gchar checker_ps_src_luma[] =
228     "static const float blocksize = 8.0;\n"
229     "static const float4 high = float4(0.667, 0.0, 0.0, 1.0);\n"
230     "static const float4 low = float4(0.333, 0.0, 0.0, 1.0);\n"
231     "struct PS_INPUT\n"
232     "{\n"
233     "  float4 Position: SV_POSITION;\n"
234     "};\n"
235     "struct PS_OUTPUT\n"
236     "{\n"
237     "  float4 Plane: SV_TARGET;\n"
238     "};\n"
239     "PS_OUTPUT main(PS_INPUT input)\n"
240     "{\n"
241     "  PS_OUTPUT output;\n"
242     "  if ((input.Position.x % (blocksize * 2.0)) >= blocksize) {\n"
243     "    if ((input.Position.y % (blocksize * 2.0)) >= blocksize)\n"
244     "      output.Plane = low;\n"
245     "    else\n"
246     "      output.Plane = high;\n"
247     "  } else {\n"
248     "    if ((input.Position.y % (blocksize * 2.0)) < blocksize)\n"
249     "      output.Plane = low;\n"
250     "    else\n"
251     "      output.Plane = high;\n"
252     "  }\n"
253     "  return output;\n"
254     "}\n";
255
256 static D3D11_RENDER_TARGET_BLEND_DESC blend_templ[] = {
257   /* SOURCE */
258   {
259     TRUE,
260     D3D11_BLEND_ONE, D3D11_BLEND_ZERO, D3D11_BLEND_OP_ADD,
261     D3D11_BLEND_ONE, D3D11_BLEND_ZERO, D3D11_BLEND_OP_ADD,
262     D3D11_COLOR_WRITE_ENABLE_ALL
263   },
264   /* OVER */
265   {
266     TRUE,
267     D3D11_BLEND_SRC_ALPHA, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
268     D3D11_BLEND_ONE, D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD,
269     D3D11_COLOR_WRITE_ENABLE_ALL,
270   },
271 };
272 /* *INDENT-ON* */
273
274 struct _GstD3D11CompositorPad
275 {
276   GstVideoAggregatorConvertPad parent;
277
278   GstD3D11Converter *convert;
279
280   gboolean position_updated;
281   gboolean alpha_updated;
282   gboolean blend_desc_updated;
283   gboolean config_updated;
284   ID3D11BlendState *blend;
285
286   D3D11_RENDER_TARGET_BLEND_DESC desc;
287
288   /* properties */
289   gint xpos;
290   gint ypos;
291   gint width;
292   gint height;
293   gdouble alpha;
294   GstD3D11CompositorOperator op;
295   GstD3D11CompositorSizingPolicy sizing_policy;
296   GstVideoGammaMode gamma_mode;
297   GstVideoPrimariesMode primaries_mode;
298 };
299
300 typedef struct
301 {
302   ID3D11PixelShader *ps;
303   ID3D11VertexShader *vs;
304   ID3D11InputLayout *layout;
305   ID3D11Buffer *vertex_buffer;
306   ID3D11Buffer *index_buffer;
307   D3D11_VIEWPORT viewport;
308 } GstD3D11CompositorQuad;
309
310 typedef struct
311 {
312   /* [rtv][colors] */
313   FLOAT color[4][4];
314 } GstD3D11CompositorClearColor;
315
316 struct _GstD3D11Compositor
317 {
318   GstVideoAggregator parent;
319
320   GstD3D11Device *device;
321
322   GstBuffer *fallback_buf;
323   GstCaps *negotiated_caps;
324
325   GstD3D11CompositorQuad *checker_background;
326   /* black/white/transparent */
327   GstD3D11CompositorClearColor clear_color[3];
328
329   gboolean downstream_supports_d3d11;
330
331   /* properties */
332   gint adapter;
333   GstD3D11CompositorBackground background;
334 };
335
336 enum
337 {
338   PROP_PAD_0,
339   PROP_PAD_XPOS,
340   PROP_PAD_YPOS,
341   PROP_PAD_WIDTH,
342   PROP_PAD_HEIGHT,
343   PROP_PAD_ALPHA,
344   PROP_PAD_OPERATOR,
345   PROP_PAD_SIZING_POLICY,
346   PROP_PAD_GAMMA_MODE,
347   PROP_PAD_PRIMARIES_MODE,
348 };
349
350 #define DEFAULT_PAD_XPOS   0
351 #define DEFAULT_PAD_YPOS   0
352 #define DEFAULT_PAD_WIDTH  0
353 #define DEFAULT_PAD_HEIGHT 0
354 #define DEFAULT_PAD_ALPHA  1.0
355 #define DEFAULT_PAD_OPERATOR GST_D3D11_COMPOSITOR_OPERATOR_OVER
356 #define DEFAULT_PAD_SIZING_POLICY GST_D3D11_COMPOSITOR_SIZING_POLICY_NONE
357 #define DEFAULT_PAD_GAMMA_MODE GST_VIDEO_GAMMA_MODE_NONE
358 #define DEFAULT_PAD_PRIMARIES_MODE GST_VIDEO_PRIMARIES_MODE_NONE
359
360 static void gst_d3d11_compositor_pad_dispose (GObject * object);
361 static void gst_d3d11_compositor_pad_set_property (GObject * object,
362     guint prop_id, const GValue * value, GParamSpec * pspec);
363 static void gst_d3d11_compositor_pad_get_property (GObject * object,
364     guint prop_id, GValue * value, GParamSpec * pspec);
365 static gboolean
366 gst_d3d11_compositor_pad_prepare_frame (GstVideoAggregatorPad * pad,
367     GstVideoAggregator * vagg, GstBuffer * buffer,
368     GstVideoFrame * prepared_frame);
369 static void gst_d3d11_compositor_pad_clean_frame (GstVideoAggregatorPad * vpad,
370     GstVideoAggregator * vagg, GstVideoFrame * prepared_frame);
371
372 #define gst_d3d11_compositor_pad_parent_class parent_pad_class
373 G_DEFINE_TYPE (GstD3D11CompositorPad, gst_d3d11_compositor_pad,
374     GST_TYPE_VIDEO_AGGREGATOR_PAD);
375
376 static void
377 gst_d3d11_compositor_pad_class_init (GstD3D11CompositorPadClass * klass)
378 {
379   GObjectClass *object_class = G_OBJECT_CLASS (klass);
380   GstVideoAggregatorPadClass *vagg_pad_class =
381       GST_VIDEO_AGGREGATOR_PAD_CLASS (klass);
382   GParamFlags param_flags = (GParamFlags)
383       (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS);
384
385   object_class->dispose = gst_d3d11_compositor_pad_dispose;
386   object_class->set_property = gst_d3d11_compositor_pad_set_property;
387   object_class->get_property = gst_d3d11_compositor_pad_get_property;
388
389   g_object_class_install_property (object_class, PROP_PAD_XPOS,
390       g_param_spec_int ("xpos", "X Position", "X position of the picture",
391           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS, param_flags));
392   g_object_class_install_property (object_class, PROP_PAD_YPOS,
393       g_param_spec_int ("ypos", "Y Position", "Y position of the picture",
394           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS, param_flags));
395   g_object_class_install_property (object_class, PROP_PAD_WIDTH,
396       g_param_spec_int ("width", "Width", "Width of the picture",
397           G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH, param_flags));
398   g_object_class_install_property (object_class, PROP_PAD_HEIGHT,
399       g_param_spec_int ("height", "Height", "Height of the picture",
400           G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT, param_flags));
401   g_object_class_install_property (object_class, PROP_PAD_ALPHA,
402       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0,
403           DEFAULT_PAD_ALPHA, param_flags));
404
405   /**
406    * GstD3D11CompositorPad:operator:
407    *
408    * Blending operator to use for blending this pad over the previous ones
409    *
410    * Since: 1.22
411    */
412   g_object_class_install_property (object_class, PROP_PAD_OPERATOR,
413       g_param_spec_enum ("operator", "Operator",
414           "Blending operator to use for blending this pad over the previous ones",
415           GST_TYPE_D3D11_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR,
416           param_flags));
417   g_object_class_install_property (object_class, PROP_PAD_SIZING_POLICY,
418       g_param_spec_enum ("sizing-policy", "Sizing policy",
419           "Sizing policy to use for image scaling",
420           GST_TYPE_D3D11_COMPOSITOR_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY,
421           param_flags));
422
423   /**
424    * GstD3D11CompositorPad:gamma-mode:
425    *
426    * Gamma conversion mode
427    *
428    * Since: 1.22
429    */
430   g_object_class_install_property (object_class, PROP_PAD_GAMMA_MODE,
431       g_param_spec_enum ("gamma-mode", "Gamma mode",
432           "Gamma conversion mode", GST_TYPE_VIDEO_GAMMA_MODE,
433           DEFAULT_PAD_GAMMA_MODE, param_flags));
434
435   /**
436    * GstD3D11CompositorPad:primaries-mode:
437    *
438    * Primaries conversion mode
439    *
440    * Since: 1.22
441    */
442   g_object_class_install_property (object_class, PROP_PAD_PRIMARIES_MODE,
443       g_param_spec_enum ("primaries-mode", "Primaries Mode",
444           "Primaries conversion mode", GST_TYPE_VIDEO_PRIMARIES_MODE,
445           DEFAULT_PAD_PRIMARIES_MODE, param_flags));
446
447   vagg_pad_class->prepare_frame =
448       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_pad_prepare_frame);
449   vagg_pad_class->clean_frame =
450       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_pad_clean_frame);
451
452   gst_type_mark_as_plugin_api (GST_TYPE_D3D11_COMPOSITOR_OPERATOR,
453       (GstPluginAPIFlags) 0);
454   gst_type_mark_as_plugin_api (GST_TYPE_D3D11_COMPOSITOR_SIZING_POLICY,
455       (GstPluginAPIFlags) 0);
456 }
457
458 static void
459 gst_d3d11_compositor_pad_init (GstD3D11CompositorPad * pad)
460 {
461   pad->xpos = DEFAULT_PAD_XPOS;
462   pad->ypos = DEFAULT_PAD_YPOS;
463   pad->width = DEFAULT_PAD_WIDTH;
464   pad->height = DEFAULT_PAD_HEIGHT;
465   pad->alpha = DEFAULT_PAD_ALPHA;
466   pad->op = DEFAULT_PAD_OPERATOR;
467   pad->sizing_policy = DEFAULT_PAD_SIZING_POLICY;
468   pad->desc = blend_templ[DEFAULT_PAD_OPERATOR];
469   pad->gamma_mode = DEFAULT_PAD_GAMMA_MODE;
470   pad->primaries_mode = DEFAULT_PAD_PRIMARIES_MODE;
471 }
472
473 static void
474 gst_d3d11_compositor_pad_dispose (GObject * object)
475 {
476   GstD3D11CompositorPad *self = GST_D3D11_COMPOSITOR_PAD (object);
477
478   gst_clear_object (&self->convert);
479   GST_D3D11_CLEAR_COM (self->blend);
480
481   G_OBJECT_CLASS (parent_pad_class)->dispose (object);
482 }
483
484 static void
485 gst_d3d11_compositor_pad_update_position (GstD3D11CompositorPad * self,
486     gint * old, const GValue * value)
487 {
488   gint tmp = g_value_get_int (value);
489
490   if (*old != tmp) {
491     *old = tmp;
492     self->position_updated = TRUE;
493   }
494 }
495
496 static void
497 gst_d3d11_compositor_pad_set_property (GObject * object, guint prop_id,
498     const GValue * value, GParamSpec * pspec)
499 {
500   GstD3D11CompositorPad *pad = GST_D3D11_COMPOSITOR_PAD (object);
501
502   switch (prop_id) {
503     case PROP_PAD_XPOS:
504       gst_d3d11_compositor_pad_update_position (pad, &pad->xpos, value);
505       break;
506     case PROP_PAD_YPOS:
507       gst_d3d11_compositor_pad_update_position (pad, &pad->ypos, value);
508       break;
509     case PROP_PAD_WIDTH:
510       gst_d3d11_compositor_pad_update_position (pad, &pad->width, value);
511       break;
512     case PROP_PAD_HEIGHT:
513       gst_d3d11_compositor_pad_update_position (pad, &pad->height, value);
514       break;
515     case PROP_PAD_ALPHA:{
516       gdouble alpha = g_value_get_double (value);
517       if (pad->alpha != alpha) {
518         pad->alpha_updated = TRUE;
519         pad->alpha = alpha;
520       }
521       break;
522     }
523     case PROP_PAD_OPERATOR:{
524       GstD3D11CompositorOperator op =
525           (GstD3D11CompositorOperator) g_value_get_enum (value);
526       if (op != pad->op) {
527         pad->op = op;
528         pad->desc = blend_templ[op];
529         pad->blend_desc_updated = TRUE;
530       }
531       break;
532     }
533     case PROP_PAD_SIZING_POLICY:{
534       GstD3D11CompositorSizingPolicy policy =
535           (GstD3D11CompositorSizingPolicy) g_value_get_enum (value);
536       if (pad->sizing_policy != policy) {
537         pad->sizing_policy = policy;
538         pad->position_updated = TRUE;
539       }
540       break;
541     }
542     case PROP_PAD_GAMMA_MODE:{
543       GstVideoGammaMode mode = (GstVideoGammaMode) g_value_get_enum (value);
544       if (pad->gamma_mode != mode) {
545         pad->gamma_mode = mode;
546         pad->config_updated = TRUE;
547       }
548       break;
549     }
550     case PROP_PAD_PRIMARIES_MODE:{
551       GstVideoPrimariesMode mode =
552           (GstVideoPrimariesMode) g_value_get_enum (value);
553       if (pad->primaries_mode != mode) {
554         pad->primaries_mode = mode;
555         pad->config_updated = TRUE;
556       }
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_compositor_pad_get_property (GObject * object, guint prop_id,
567     GValue * value, GParamSpec * pspec)
568 {
569   GstD3D11CompositorPad *pad = GST_D3D11_COMPOSITOR_PAD (object);
570
571   switch (prop_id) {
572     case PROP_PAD_XPOS:
573       g_value_set_int (value, pad->xpos);
574       break;
575     case PROP_PAD_YPOS:
576       g_value_set_int (value, pad->ypos);
577       break;
578     case PROP_PAD_WIDTH:
579       g_value_set_int (value, pad->width);
580       break;
581     case PROP_PAD_HEIGHT:
582       g_value_set_int (value, pad->height);
583       break;
584     case PROP_PAD_ALPHA:
585       g_value_set_double (value, pad->alpha);
586       break;
587     case PROP_PAD_OPERATOR:
588       g_value_set_enum (value, pad->op);
589       break;
590     case PROP_PAD_SIZING_POLICY:
591       g_value_set_enum (value, pad->sizing_policy);
592       break;
593     case PROP_PAD_GAMMA_MODE:
594       g_value_set_enum (value, pad->gamma_mode);
595       break;
596     case PROP_PAD_PRIMARIES_MODE:
597       g_value_set_enum (value, pad->primaries_mode);
598       break;
599     default:
600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601       break;
602   }
603 }
604
605 static void
606 gst_d3d11_compositor_pad_get_output_size (GstD3D11CompositorPad * comp_pad,
607     gint out_par_n, gint out_par_d, gint * width, gint * height,
608     gint * x_offset, gint * y_offset)
609 {
610   GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (comp_pad);
611   gint pad_width, pad_height;
612   guint dar_n, dar_d;
613
614   *x_offset = 0;
615   *y_offset = 0;
616   *width = 0;
617   *height = 0;
618
619   /* FIXME: Anything better we can do here? */
620   if (!vagg_pad->info.finfo
621       || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) {
622     GST_DEBUG_OBJECT (comp_pad, "Have no caps yet");
623     return;
624   }
625
626   pad_width =
627       comp_pad->width <=
628       0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width;
629   pad_height =
630       comp_pad->height <=
631       0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height;
632
633   if (pad_width == 0 || pad_height == 0)
634     return;
635
636   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height,
637           GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
638           GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) {
639     GST_WARNING_OBJECT (comp_pad, "Cannot calculate display aspect ratio");
640     return;
641   }
642
643   GST_TRACE_OBJECT (comp_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)",
644       pad_width, pad_height, dar_n, dar_d,
645       GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
646       GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d);
647
648   switch (comp_pad->sizing_policy) {
649     case GST_D3D11_COMPOSITOR_SIZING_POLICY_NONE:
650       /* Pick either height or width, whichever is an integer multiple of the
651        * display aspect ratio. However, prefer preserving the height to account
652        * for interlaced video. */
653       if (pad_height % dar_n == 0) {
654         pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
655       } else if (pad_width % dar_d == 0) {
656         pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n);
657       } else {
658         pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d);
659       }
660       break;
661     case GST_D3D11_COMPOSITOR_SIZING_POLICY_KEEP_ASPECT_RATIO:{
662       gint from_dar_n, from_dar_d, to_dar_n, to_dar_d, num, den;
663
664       /* Calculate DAR again with actual video size */
665       if (!gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (&vagg_pad->info),
666               GST_VIDEO_INFO_HEIGHT (&vagg_pad->info),
667               GST_VIDEO_INFO_PAR_N (&vagg_pad->info),
668               GST_VIDEO_INFO_PAR_D (&vagg_pad->info), &from_dar_n,
669               &from_dar_d)) {
670         from_dar_n = from_dar_d = -1;
671       }
672
673       if (!gst_util_fraction_multiply (pad_width, pad_height,
674               out_par_n, out_par_d, &to_dar_n, &to_dar_d)) {
675         to_dar_n = to_dar_d = -1;
676       }
677
678       if (from_dar_n != to_dar_n || from_dar_d != to_dar_d) {
679         /* Calculate new output resolution */
680         if (from_dar_n != -1 && from_dar_d != -1
681             && gst_util_fraction_multiply (from_dar_n, from_dar_d,
682                 out_par_d, out_par_n, &num, &den)) {
683           GstVideoRectangle src_rect, dst_rect, rst_rect;
684
685           src_rect.h = gst_util_uint64_scale_int (pad_width, den, num);
686           if (src_rect.h == 0) {
687             pad_width = 0;
688             pad_height = 0;
689             break;
690           }
691
692           src_rect.x = src_rect.y = 0;
693           src_rect.w = pad_width;
694
695           dst_rect.x = dst_rect.y = 0;
696           dst_rect.w = pad_width;
697           dst_rect.h = pad_height;
698
699           /* Scale rect to be centered in destination rect */
700           gst_video_center_rect (&src_rect, &dst_rect, &rst_rect, TRUE);
701
702           GST_LOG_OBJECT (comp_pad,
703               "Re-calculated size %dx%d -> %dx%d (x-offset %d, y-offset %d)",
704               pad_width, pad_height, rst_rect.w, rst_rect.h, rst_rect.x,
705               rst_rect.h);
706
707           *x_offset = rst_rect.x;
708           *y_offset = rst_rect.y;
709           pad_width = rst_rect.w;
710           pad_height = rst_rect.h;
711         } else {
712           GST_WARNING_OBJECT (comp_pad, "Failed to calculate output size");
713
714           *x_offset = 0;
715           *y_offset = 0;
716           pad_width = 0;
717           pad_height = 0;
718         }
719       }
720       break;
721     }
722   }
723
724   *width = pad_width;
725   *height = pad_height;
726 }
727
728 static GstVideoRectangle
729 clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width,
730     gint outer_height)
731 {
732   gint x2 = x + w;
733   gint y2 = y + h;
734   GstVideoRectangle clamped;
735
736   /* Clamp the x/y coordinates of this frame to the output boundaries to cover
737    * the case where (say, with negative xpos/ypos or w/h greater than the output
738    * size) the non-obscured portion of the frame could be outside the bounds of
739    * the video itself and hence not visible at all */
740   clamped.x = CLAMP (x, 0, outer_width);
741   clamped.y = CLAMP (y, 0, outer_height);
742   clamped.w = CLAMP (x2, 0, outer_width) - clamped.x;
743   clamped.h = CLAMP (y2, 0, outer_height) - clamped.y;
744
745   return clamped;
746 }
747
748 static gboolean
749 gst_d3d11_compositor_pad_check_frame_obscured (GstVideoAggregatorPad * pad,
750     GstVideoAggregator * vagg)
751 {
752   GstD3D11CompositorPad *cpad = GST_D3D11_COMPOSITOR_PAD (pad);
753   gint width, height;
754   GstVideoInfo *info = &vagg->info;
755   /* The rectangle representing this frame, clamped to the video's boundaries.
756    * Due to the clamping, this is different from the frame width/height above. */
757   GstVideoRectangle frame_rect;
758   gint x_offset, y_offset;
759
760   /* There's three types of width/height here:
761    * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT:
762    *     The frame width/height (same as pad->info.height/width;
763    *     see gst_video_frame_map())
764    * 2. cpad->width/height:
765    *     The optional pad property for scaling the frame (if zero, the video is
766    *     left unscaled)
767    */
768
769   gst_d3d11_compositor_pad_get_output_size (cpad, GST_VIDEO_INFO_PAR_N (info),
770       GST_VIDEO_INFO_PAR_D (info), &width, &height, &x_offset, &y_offset);
771
772   frame_rect = clamp_rectangle (cpad->xpos + x_offset, cpad->ypos + y_offset,
773       width, height, GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
774
775   if (frame_rect.w == 0 || frame_rect.h == 0) {
776     GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height "
777         "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h);
778     return TRUE;
779   }
780
781   return FALSE;
782 }
783
784 static gboolean
785 gst_d3d11_compositor_pad_prepare_frame (GstVideoAggregatorPad * pad,
786     GstVideoAggregator * vagg, GstBuffer * buffer,
787     GstVideoFrame * prepared_frame)
788 {
789   /* Skip this frame */
790   if (gst_d3d11_compositor_pad_check_frame_obscured (pad, vagg))
791     return TRUE;
792
793   /* don't map/upload now, it will happen in converter object.
794    * Just mark this frame is preparted instead */
795   prepared_frame->buffer = buffer;
796
797   return TRUE;
798 }
799
800 static void
801 gst_d3d11_compositor_pad_clean_frame (GstVideoAggregatorPad * vpad,
802     GstVideoAggregator * vagg, GstVideoFrame * prepared_frame)
803 {
804   memset (prepared_frame, 0, sizeof (GstVideoFrame));
805 }
806
807 static gboolean
808 gst_d3d11_compositor_pad_setup_converter (GstVideoAggregatorPad * pad,
809     GstVideoAggregator * vagg)
810 {
811   GstD3D11CompositorPad *cpad = GST_D3D11_COMPOSITOR_PAD (pad);
812   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (vagg);
813   gint width, height;
814   GstVideoInfo *info = &vagg->info;
815   GstVideoRectangle frame_rect;
816   gboolean is_first = FALSE;
817   gboolean output_has_alpha_comp = FALSE;
818   gint x_offset, y_offset;
819 #ifndef GST_DISABLE_GST_DEBUG
820   guint zorder = 0;
821 #endif
822   static const D3D11_RENDER_TARGET_BLEND_DESC blend_over_no_alpha = {
823     TRUE,
824     D3D11_BLEND_BLEND_FACTOR, D3D11_BLEND_INV_BLEND_FACTOR, D3D11_BLEND_OP_ADD,
825     D3D11_BLEND_BLEND_FACTOR, D3D11_BLEND_INV_BLEND_FACTOR, D3D11_BLEND_OP_ADD,
826     D3D11_COLOR_WRITE_ENABLE_ALL,
827   };
828
829   if (GST_VIDEO_INFO_HAS_ALPHA (info) ||
830       GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_BGRx ||
831       GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_RGBx) {
832     output_has_alpha_comp = TRUE;
833   }
834
835   if (cpad->config_updated) {
836     gst_clear_object (&cpad->convert);
837     cpad->config_updated = FALSE;
838   }
839
840   if (!cpad->convert) {
841     GstStructure *config = gst_structure_new ("converter-config",
842         /* XXX: Always use shader, to workaround buggy blending behavior of
843          * vendor implemented converter. Need investigation */
844         GST_D3D11_CONVERTER_OPT_BACKEND, GST_TYPE_D3D11_CONVERTER_BACKEND,
845         GST_D3D11_CONVERTER_BACKEND_SHADER,
846         GST_D3D11_CONVERTER_OPT_GAMMA_MODE,
847         GST_TYPE_VIDEO_GAMMA_MODE, cpad->gamma_mode,
848         GST_D3D11_CONVERTER_OPT_PRIMARIES_MODE,
849         GST_TYPE_VIDEO_PRIMARIES_MODE, cpad->primaries_mode, nullptr);
850
851     cpad->convert = gst_d3d11_converter_new (self->device, &pad->info, info,
852         config);
853     if (!cpad->convert) {
854       GST_ERROR_OBJECT (pad, "Couldn't create converter");
855       return FALSE;
856     }
857
858     is_first = TRUE;
859   }
860
861   if (cpad->alpha_updated || is_first) {
862     if (output_has_alpha_comp) {
863       g_object_set (cpad->convert, "alpha", cpad->alpha, nullptr);
864     } else {
865       gfloat blend_factor = cpad->alpha;
866
867       g_object_set (cpad->convert,
868           "blend-factor-red", blend_factor,
869           "blend-factor-green", blend_factor,
870           "blend-factor-blue", blend_factor,
871           "blend-factor-alpha", blend_factor, nullptr);
872     }
873
874     cpad->alpha_updated = FALSE;
875   }
876
877   if (!cpad->blend || cpad->blend_desc_updated || is_first) {
878     HRESULT hr;
879     D3D11_BLEND_DESC desc = { 0, };
880     ID3D11BlendState *blend = nullptr;
881     ID3D11Device *device_handle =
882         gst_d3d11_device_get_device_handle (self->device);
883     gfloat blend_factor = 1.0f;
884
885     GST_D3D11_CLEAR_COM (cpad->blend);
886
887     desc.AlphaToCoverageEnable = FALSE;
888     desc.IndependentBlendEnable = FALSE;
889     desc.RenderTarget[0] = cpad->desc;
890     if (!output_has_alpha_comp &&
891         cpad->op == GST_D3D11_COMPOSITOR_OPERATOR_OVER) {
892       desc.RenderTarget[0] = blend_over_no_alpha;
893       blend_factor = cpad->alpha;
894     }
895
896     hr = device_handle->CreateBlendState (&desc, &blend);
897     if (!gst_d3d11_result (hr, self->device)) {
898       GST_ERROR_OBJECT (pad, "Couldn't create blend staten, hr: 0x%x",
899           (guint) hr);
900       return FALSE;
901     }
902
903     cpad->blend = blend;
904     g_object_set (cpad->convert, "blend-state", blend,
905         "blend-factor-red", blend_factor,
906         "blend-factor-green", blend_factor,
907         "blend-factor-blue", blend_factor,
908         "blend-factor-alpha", blend_factor, nullptr);
909
910     cpad->blend_desc_updated = FALSE;
911   }
912
913   if (!is_first && !cpad->position_updated)
914     return TRUE;
915
916   gst_d3d11_compositor_pad_get_output_size (cpad, GST_VIDEO_INFO_PAR_N (info),
917       GST_VIDEO_INFO_PAR_D (info), &width, &height, &x_offset, &y_offset);
918
919   frame_rect = clamp_rectangle (cpad->xpos + x_offset, cpad->ypos + y_offset,
920       width, height, GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
921
922 #ifndef GST_DISABLE_GST_DEBUG
923   g_object_get (pad, "zorder", &zorder, nullptr);
924
925   GST_LOG_OBJECT (pad, "Update position, pad-xpos %d, pad-ypos %d, "
926       "pad-zorder %d, pad-width %d, pad-height %d, in-resolution %dx%d, "
927       "out-resoution %dx%d, dst-{x,y,width,height} %d-%d-%d-%d",
928       cpad->xpos, cpad->ypos, zorder, cpad->width, cpad->height,
929       GST_VIDEO_INFO_WIDTH (&pad->info), GST_VIDEO_INFO_HEIGHT (&pad->info),
930       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
931       frame_rect.x, frame_rect.y, frame_rect.w, frame_rect.h);
932 #endif
933
934   cpad->position_updated = FALSE;
935
936   g_object_set (cpad->convert, "dest-x", frame_rect.x,
937       "dest-y", frame_rect.y, "dest-width", frame_rect.w,
938       "dest-height", frame_rect.h, nullptr);
939
940   return TRUE;
941 }
942
943 static GstStaticCaps sink_pad_template_caps =
944     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
945     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, GST_D3D11_SINK_FORMATS) "; "
946     GST_VIDEO_CAPS_MAKE (GST_D3D11_SINK_FORMATS));
947
948 /* formats we can output without conversion.
949  * Excludes 10/12 bits planar YUV (needs bitshift) and
950  * AYUV/AYUV64 (d3d11 runtime does not understand the ayuv order) */
951 #define COMPOSITOR_SRC_FORMATS \
952     "{ RGBA64_LE, RGB10A2_LE, BGRA, RGBA, BGRx, RGBx, VUYA, NV12, NV21, " \
953     "P010_10LE, P012_LE, P016_LE, I420, YV12, Y42B, Y444, Y444_16LE, " \
954     "GRAY8, GRAY16_LE }"
955
956 static GstStaticCaps src_pad_template_caps =
957     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
958     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, COMPOSITOR_SRC_FORMATS) "; "
959     GST_VIDEO_CAPS_MAKE (COMPOSITOR_SRC_FORMATS));
960
961 enum
962 {
963   PROP_0,
964   PROP_ADAPTER,
965   PROP_BACKGROUND,
966 };
967
968 #define DEFAULT_ADAPTER -1
969 #define DEFAULT_BACKGROUND GST_D3D11_COMPOSITOR_BACKGROUND_CHECKER
970
971 static void gst_d3d11_compositor_child_proxy_init (gpointer g_iface,
972     gpointer iface_data);
973 static void gst_d3d11_compositor_dispose (GObject * object);
974 static void gst_d3d11_compositor_set_property (GObject * object,
975     guint prop_id, const GValue * value, GParamSpec * pspec);
976 static void gst_d3d11_compositor_get_property (GObject * object,
977     guint prop_id, GValue * value, GParamSpec * pspec);
978
979 static GstPad *gst_d3d11_compositor_request_new_pad (GstElement * element,
980     GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
981 static void gst_d3d11_compositor_release_pad (GstElement * element,
982     GstPad * pad);
983 static void gst_d3d11_compositor_set_context (GstElement * element,
984     GstContext * context);
985
986 static gboolean gst_d3d11_compositor_start (GstAggregator * agg);
987 static gboolean gst_d3d11_compositor_stop (GstAggregator * agg);
988 static gboolean gst_d3d11_compositor_sink_query (GstAggregator * agg,
989     GstAggregatorPad * pad, GstQuery * query);
990 static gboolean gst_d3d11_compositor_src_query (GstAggregator * agg,
991     GstQuery * query);
992 static GstCaps *gst_d3d11_compositor_fixate_src_caps (GstAggregator * agg,
993     GstCaps * caps);
994 static gboolean gst_d3d11_compositor_negotiated_src_caps (GstAggregator * agg,
995     GstCaps * caps);
996 static gboolean
997 gst_d3d11_compositor_propose_allocation (GstAggregator * agg,
998     GstAggregatorPad * pad, GstQuery * decide_query, GstQuery * query);
999 static gboolean gst_d3d11_compositor_decide_allocation (GstAggregator * agg,
1000     GstQuery * query);
1001 static GstFlowReturn
1002 gst_d3d11_compositor_aggregate_frames (GstVideoAggregator * vagg,
1003     GstBuffer * outbuf);
1004 static GstFlowReturn
1005 gst_d3d11_compositor_create_output_buffer (GstVideoAggregator * vagg,
1006     GstBuffer ** outbuffer);
1007 static void gst_d3d11_compositor_quad_free (GstD3D11CompositorQuad * quad);
1008
1009 #define gst_d3d11_compositor_parent_class parent_class
1010 G_DEFINE_TYPE_WITH_CODE (GstD3D11Compositor, gst_d3d11_compositor,
1011     GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
1012         gst_d3d11_compositor_child_proxy_init));
1013
1014 static void
1015 gst_d3d11_compositor_class_init (GstD3D11CompositorClass * klass)
1016 {
1017   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1018   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
1019   GstAggregatorClass *agg_class = GST_AGGREGATOR_CLASS (klass);
1020   GstVideoAggregatorClass *vagg_class = GST_VIDEO_AGGREGATOR_CLASS (klass);
1021   GstCaps *caps;
1022
1023   object_class->dispose = gst_d3d11_compositor_dispose;
1024   object_class->set_property = gst_d3d11_compositor_set_property;
1025   object_class->get_property = gst_d3d11_compositor_get_property;
1026
1027   g_object_class_install_property (object_class, PROP_ADAPTER,
1028       g_param_spec_int ("adapter", "Adapter",
1029           "Adapter index for creating device (-1 for default)",
1030           -1, G_MAXINT32, DEFAULT_ADAPTER,
1031           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
1032               G_PARAM_STATIC_STRINGS)));
1033
1034   g_object_class_install_property (object_class, PROP_BACKGROUND,
1035       g_param_spec_enum ("background", "Background", "Background type",
1036           GST_TYPE_D3D11_COMPOSITOR_BACKGROUND,
1037           DEFAULT_BACKGROUND,
1038           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
1039
1040   element_class->request_new_pad =
1041       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_request_new_pad);
1042   element_class->release_pad =
1043       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_release_pad);
1044   element_class->set_context =
1045       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_set_context);
1046
1047   agg_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_compositor_start);
1048   agg_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_compositor_stop);
1049   agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_d3d11_compositor_sink_query);
1050   agg_class->src_query = GST_DEBUG_FUNCPTR (gst_d3d11_compositor_src_query);
1051   agg_class->fixate_src_caps =
1052       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_fixate_src_caps);
1053   agg_class->negotiated_src_caps =
1054       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_negotiated_src_caps);
1055   agg_class->propose_allocation =
1056       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_propose_allocation);
1057   agg_class->decide_allocation =
1058       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_decide_allocation);
1059
1060   vagg_class->aggregate_frames =
1061       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_aggregate_frames);
1062   vagg_class->create_output_buffer =
1063       GST_DEBUG_FUNCPTR (gst_d3d11_compositor_create_output_buffer);
1064
1065   caps = gst_d3d11_get_updated_template_caps (&sink_pad_template_caps);
1066   gst_element_class_add_pad_template (element_class,
1067       gst_pad_template_new_with_gtype ("sink_%u", GST_PAD_SINK, GST_PAD_REQUEST,
1068           caps, GST_TYPE_D3D11_COMPOSITOR_PAD));
1069   gst_caps_unref (caps);
1070
1071   caps = gst_d3d11_get_updated_template_caps (&src_pad_template_caps);
1072   gst_element_class_add_pad_template (element_class,
1073       gst_pad_template_new_with_gtype ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
1074           caps, GST_TYPE_AGGREGATOR_PAD));
1075   gst_caps_unref (caps);
1076
1077   gst_element_class_set_static_metadata (element_class, "Direct3D11 Compositor",
1078       "Filter/Editor/Video/Compositor", "A Direct3D11 compositor",
1079       "Seungha Yang <seungha@centricular.com>");
1080
1081   gst_type_mark_as_plugin_api (GST_TYPE_D3D11_COMPOSITOR_BACKGROUND,
1082       (GstPluginAPIFlags) 0);
1083   gst_type_mark_as_plugin_api (GST_TYPE_D3D11_COMPOSITOR_PAD,
1084       (GstPluginAPIFlags) 0);
1085
1086   GST_DEBUG_CATEGORY_INIT (gst_d3d11_compositor_debug,
1087       "d3d11compositor", 0, "d3d11compositor element");
1088 }
1089
1090 static void
1091 gst_d3d11_compositor_init (GstD3D11Compositor * self)
1092 {
1093   self->adapter = DEFAULT_ADAPTER;
1094   self->background = DEFAULT_BACKGROUND;
1095 }
1096
1097 static void
1098 gst_d3d11_compositor_dispose (GObject * object)
1099 {
1100   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (object);
1101
1102   gst_clear_object (&self->device);
1103   gst_clear_buffer (&self->fallback_buf);
1104   g_clear_pointer (&self->checker_background, gst_d3d11_compositor_quad_free);
1105
1106   G_OBJECT_CLASS (parent_class)->dispose (object);
1107 }
1108
1109 static void
1110 gst_d3d11_compositor_set_property (GObject * object,
1111     guint prop_id, const GValue * value, GParamSpec * pspec)
1112 {
1113   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (object);
1114
1115   switch (prop_id) {
1116     case PROP_ADAPTER:
1117       self->adapter = g_value_get_int (value);
1118       break;
1119     case PROP_BACKGROUND:
1120       self->background =
1121           (GstD3D11CompositorBackground) g_value_get_enum (value);
1122       break;
1123     default:
1124       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1125       break;
1126   }
1127 }
1128
1129 static void
1130 gst_d3d11_compositor_get_property (GObject * object,
1131     guint prop_id, GValue * value, GParamSpec * pspec)
1132 {
1133   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (object);
1134
1135   switch (prop_id) {
1136     case PROP_ADAPTER:
1137       g_value_set_int (value, self->adapter);
1138       break;
1139     case PROP_BACKGROUND:
1140       g_value_set_enum (value, self->background);
1141       break;
1142     default:
1143       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1144       break;
1145   }
1146 }
1147
1148 static GObject *
1149 gst_d3d11_compositor_child_proxy_get_child_by_index (GstChildProxy * proxy,
1150     guint index)
1151 {
1152   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (proxy);
1153   GObject *obj = nullptr;
1154
1155   GST_OBJECT_LOCK (self);
1156   obj = (GObject *) g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index);
1157   if (obj)
1158     gst_object_ref (obj);
1159   GST_OBJECT_UNLOCK (self);
1160
1161   return obj;
1162 }
1163
1164 static guint
1165 gst_d3d11_compositor_child_proxy_get_children_count (GstChildProxy * proxy)
1166 {
1167   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (proxy);
1168   guint count = 0;
1169
1170   GST_OBJECT_LOCK (self);
1171   count = GST_ELEMENT_CAST (self)->numsinkpads;
1172   GST_OBJECT_UNLOCK (self);
1173   GST_INFO_OBJECT (self, "Children Count: %d", count);
1174
1175   return count;
1176 }
1177
1178 static void
1179 gst_d3d11_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data)
1180 {
1181   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_iface;
1182
1183   iface->get_child_by_index =
1184       gst_d3d11_compositor_child_proxy_get_child_by_index;
1185   iface->get_children_count =
1186       gst_d3d11_compositor_child_proxy_get_children_count;
1187 }
1188
1189 static GstPad *
1190 gst_d3d11_compositor_request_new_pad (GstElement * element,
1191     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
1192 {
1193   GstPad *pad;
1194
1195   pad = GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
1196       templ, name, caps);
1197
1198   if (!pad) {
1199     GST_DEBUG_OBJECT (element, "could not create/add pad");
1200     return nullptr;
1201   }
1202
1203   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (pad),
1204       GST_OBJECT_NAME (pad));
1205
1206   GST_DEBUG_OBJECT (element, "Created new pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1207
1208   return pad;
1209 }
1210
1211 static gboolean
1212 gst_d3d11_compositor_pad_clear_resource (GstD3D11Compositor * self,
1213     GstD3D11CompositorPad * cpad, gpointer user_data)
1214 {
1215   gst_clear_object (&cpad->convert);
1216   GST_D3D11_CLEAR_COM (cpad->blend);
1217
1218   return TRUE;
1219 }
1220
1221 static void
1222 gst_d3d11_compositor_release_pad (GstElement * element, GstPad * pad)
1223 {
1224   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (element);
1225
1226   GST_DEBUG_OBJECT (self, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1227
1228   gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad),
1229       GST_OBJECT_NAME (pad));
1230
1231   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
1232 }
1233
1234 static void
1235 gst_d3d11_compositor_set_context (GstElement * element, GstContext * context)
1236 {
1237   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (element);
1238
1239   gst_d3d11_handle_set_context (element, context, self->adapter, &self->device);
1240
1241   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1242 }
1243
1244 static gboolean
1245 gst_d3d11_compositor_start (GstAggregator * agg)
1246 {
1247   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1248
1249   if (!gst_d3d11_ensure_element_data (GST_ELEMENT_CAST (self),
1250           self->adapter, &self->device)) {
1251     GST_ERROR_OBJECT (self, "Failed to get D3D11 device");
1252     return FALSE;
1253   }
1254
1255   return GST_AGGREGATOR_CLASS (parent_class)->start (agg);
1256 }
1257
1258 static gboolean
1259 gst_d3d11_compositor_stop (GstAggregator * agg)
1260 {
1261   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1262
1263   g_clear_pointer (&self->checker_background, gst_d3d11_compositor_quad_free);
1264   gst_clear_object (&self->device);
1265   gst_clear_caps (&self->negotiated_caps);
1266
1267   return GST_AGGREGATOR_CLASS (parent_class)->stop (agg);
1268 }
1269
1270 static GstCaps *
1271 gst_d3d11_compositor_sink_getcaps (GstPad * pad, GstCaps * filter)
1272 {
1273   GstCaps *sinkcaps;
1274   GstCaps *template_caps;
1275   GstCaps *filtered_caps;
1276   GstCaps *returned_caps;
1277
1278   template_caps = gst_pad_get_pad_template_caps (pad);
1279
1280   sinkcaps = gst_pad_get_current_caps (pad);
1281   if (sinkcaps == nullptr) {
1282     sinkcaps = gst_caps_ref (template_caps);
1283   } else {
1284     sinkcaps = gst_caps_merge (sinkcaps, gst_caps_ref (template_caps));
1285   }
1286
1287   if (filter) {
1288     filtered_caps = gst_caps_intersect (sinkcaps, filter);
1289     gst_caps_unref (sinkcaps);
1290   } else {
1291     filtered_caps = sinkcaps;   /* pass ownership */
1292   }
1293
1294   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
1295
1296   gst_caps_unref (template_caps);
1297   gst_caps_unref (filtered_caps);
1298
1299   GST_DEBUG_OBJECT (pad, "returning %" GST_PTR_FORMAT, returned_caps);
1300
1301   return returned_caps;
1302 }
1303
1304 static gboolean
1305 gst_d3d11_compositor_sink_acceptcaps (GstPad * pad, GstCaps * caps)
1306 {
1307   gboolean ret;
1308   GstCaps *template_caps;
1309
1310   GST_DEBUG_OBJECT (pad, "try accept caps of %" GST_PTR_FORMAT, caps);
1311
1312   template_caps = gst_pad_get_pad_template_caps (pad);
1313   template_caps = gst_caps_make_writable (template_caps);
1314
1315   ret = gst_caps_can_intersect (caps, template_caps);
1316   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
1317       (ret ? "" : "not "), caps);
1318   gst_caps_unref (template_caps);
1319
1320   return ret;
1321 }
1322
1323 static gboolean
1324 gst_d3d11_compositor_sink_query (GstAggregator * agg,
1325     GstAggregatorPad * pad, GstQuery * query)
1326 {
1327   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1328
1329   switch (GST_QUERY_TYPE (query)) {
1330     case GST_QUERY_CONTEXT:
1331       if (gst_d3d11_handle_context_query (GST_ELEMENT (agg), query,
1332               self->device)) {
1333         return TRUE;
1334       }
1335       break;
1336     case GST_QUERY_CAPS:{
1337       GstCaps *filter, *caps;
1338
1339       gst_query_parse_caps (query, &filter);
1340       caps = gst_d3d11_compositor_sink_getcaps (GST_PAD (pad), filter);
1341       gst_query_set_caps_result (query, caps);
1342       gst_caps_unref (caps);
1343       return TRUE;
1344     }
1345     case GST_QUERY_ACCEPT_CAPS:{
1346       GstCaps *caps;
1347       gboolean ret;
1348
1349       gst_query_parse_accept_caps (query, &caps);
1350       ret = gst_d3d11_compositor_sink_acceptcaps (GST_PAD (pad), caps);
1351       gst_query_set_accept_caps_result (query, ret);
1352       return TRUE;
1353     }
1354     default:
1355       break;
1356   }
1357
1358   return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, pad, query);
1359 }
1360
1361 static gboolean
1362 gst_d3d11_compositor_src_query (GstAggregator * agg, GstQuery * query)
1363 {
1364   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1365
1366   switch (GST_QUERY_TYPE (query)) {
1367     case GST_QUERY_CONTEXT:
1368       if (gst_d3d11_handle_context_query (GST_ELEMENT (agg), query,
1369               self->device)) {
1370         return TRUE;
1371       }
1372       break;
1373     default:
1374       break;
1375   }
1376
1377   return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
1378 }
1379
1380 static GstCaps *
1381 gst_d3d11_compositor_fixate_src_caps (GstAggregator * agg, GstCaps * caps)
1382 {
1383   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
1384   GList *l;
1385   gint best_width = -1, best_height = -1;
1386   gint best_fps_n = -1, best_fps_d = -1;
1387   gint par_n, par_d;
1388   gdouble best_fps = 0.;
1389   GstCaps *ret = nullptr;
1390   GstStructure *s;
1391
1392   ret = gst_caps_make_writable (caps);
1393
1394   /* we need this to calculate how large to make the output frame */
1395   s = gst_caps_get_structure (ret, 0);
1396   if (gst_structure_has_field (s, "pixel-aspect-ratio")) {
1397     gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1);
1398     gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
1399   } else {
1400     par_n = par_d = 1;
1401   }
1402
1403   GST_OBJECT_LOCK (vagg);
1404   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
1405     GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (l->data);
1406     GstD3D11CompositorPad *cpad = GST_D3D11_COMPOSITOR_PAD (vaggpad);
1407     gint this_width, this_height;
1408     gint width, height;
1409     gint fps_n, fps_d;
1410     gdouble cur_fps;
1411     gint x_offset;
1412     gint y_offset;
1413
1414     fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info);
1415     fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info);
1416     gst_d3d11_compositor_pad_get_output_size (cpad,
1417         par_n, par_d, &width, &height, &x_offset, &y_offset);
1418
1419     if (width == 0 || height == 0)
1420       continue;
1421
1422     /* {x,y}_offset represent padding size of each top and left area.
1423      * To calculate total resolution, count bottom and right padding area
1424      * as well here */
1425     this_width = width + MAX (cpad->xpos + 2 * x_offset, 0);
1426     this_height = height + MAX (cpad->ypos + 2 * y_offset, 0);
1427
1428     if (best_width < this_width)
1429       best_width = this_width;
1430     if (best_height < this_height)
1431       best_height = this_height;
1432
1433     if (fps_d == 0)
1434       cur_fps = 0.0;
1435     else
1436       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
1437
1438     if (best_fps < cur_fps) {
1439       best_fps = cur_fps;
1440       best_fps_n = fps_n;
1441       best_fps_d = fps_d;
1442     }
1443   }
1444   GST_OBJECT_UNLOCK (vagg);
1445
1446   if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) {
1447     best_fps_n = 25;
1448     best_fps_d = 1;
1449     best_fps = 25.0;
1450   }
1451
1452   gst_structure_fixate_field_nearest_int (s, "width", best_width);
1453   gst_structure_fixate_field_nearest_int (s, "height", best_height);
1454   gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n,
1455       best_fps_d);
1456   ret = gst_caps_fixate (ret);
1457
1458   GST_LOG_OBJECT (agg, "Fixated caps %" GST_PTR_FORMAT, ret);
1459
1460   return ret;
1461 }
1462
1463 static void
1464 convert_info_gray_to_yuv (const GstVideoInfo * gray, GstVideoInfo * yuv)
1465 {
1466   GstVideoInfo tmp;
1467
1468   if (GST_VIDEO_INFO_IS_YUV (gray)) {
1469     *yuv = *gray;
1470     return;
1471   }
1472
1473   if (gray->finfo->depth[0] == 8) {
1474     gst_video_info_set_format (&tmp,
1475         GST_VIDEO_FORMAT_Y444, gray->width, gray->height);
1476   } else {
1477     gst_video_info_set_format (&tmp,
1478         GST_VIDEO_FORMAT_Y444_16LE, gray->width, gray->height);
1479   }
1480
1481   tmp.colorimetry.range = gray->colorimetry.range;
1482   if (tmp.colorimetry.range == GST_VIDEO_COLOR_RANGE_UNKNOWN)
1483     tmp.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
1484
1485   tmp.colorimetry.primaries = gray->colorimetry.primaries;
1486   if (tmp.colorimetry.primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN)
1487     tmp.colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
1488
1489   tmp.colorimetry.transfer = gray->colorimetry.transfer;
1490   if (tmp.colorimetry.transfer == GST_VIDEO_TRANSFER_UNKNOWN)
1491     tmp.colorimetry.transfer = GST_VIDEO_TRANSFER_BT709;
1492
1493   tmp.colorimetry.matrix = gray->colorimetry.matrix;
1494   if (tmp.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN)
1495     tmp.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
1496
1497   *yuv = tmp;
1498 }
1499
1500 static void
1501 gst_d3d11_compositor_calculate_background_color (GstD3D11Compositor * self,
1502     const GstVideoInfo * info)
1503 {
1504   GstD3D11ColorMatrix clear_color_matrix;
1505   gdouble rgb[3];
1506   gdouble converted[3];
1507   GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
1508
1509   if (GST_VIDEO_INFO_IS_RGB (info)) {
1510     GstVideoInfo rgb_info = *info;
1511     rgb_info.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
1512
1513     gst_d3d11_color_range_adjust_matrix_unorm (&rgb_info, info,
1514         &clear_color_matrix);
1515   } else {
1516     GstVideoInfo rgb_info;
1517     GstVideoInfo yuv_info;
1518
1519     gst_video_info_set_format (&rgb_info, GST_VIDEO_FORMAT_RGBA64_LE,
1520         info->width, info->height);
1521     convert_info_gray_to_yuv (info, &yuv_info);
1522
1523     if (yuv_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN ||
1524         yuv_info.colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
1525       GST_WARNING_OBJECT (self, "Invalid matrix is detected");
1526       yuv_info.colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
1527     }
1528
1529     gst_d3d11_rgb_to_yuv_matrix_unorm (&rgb_info,
1530         &yuv_info, &clear_color_matrix);
1531   }
1532
1533   /* Calculate black and white color values */
1534   for (guint i = 0; i < 2; i++) {
1535     GstD3D11CompositorClearColor *clear_color = &self->clear_color[i];
1536     rgb[0] = rgb[1] = rgb[2] = (gdouble) i;
1537
1538     for (guint j = 0; j < 3; j++) {
1539       converted[j] = 0;
1540       for (guint k = 0; k < 3; k++) {
1541         converted[j] += clear_color_matrix.matrix[j][k] * rgb[k];
1542       }
1543       converted[j] += clear_color_matrix.offset[j];
1544       converted[j] = CLAMP (converted[j],
1545           clear_color_matrix.min[j], clear_color_matrix.max[j]);
1546     }
1547
1548     GST_DEBUG_OBJECT (self, "Calculated background color RGB: %f, %f, %f",
1549         converted[0], converted[1], converted[2]);
1550
1551     if (GST_VIDEO_INFO_IS_RGB (info) || GST_VIDEO_INFO_IS_GRAY (info)) {
1552       for (guint j = 0; j < 3; j++)
1553         clear_color->color[0][j] = converted[j];
1554       clear_color->color[0][3] = 1.0;
1555     } else {
1556       switch (format) {
1557         case GST_VIDEO_FORMAT_VUYA:
1558           clear_color->color[0][0] = converted[2];
1559           clear_color->color[0][1] = converted[1];
1560           clear_color->color[0][2] = converted[0];
1561           clear_color->color[0][3] = 1.0;
1562           break;
1563         case GST_VIDEO_FORMAT_NV12:
1564         case GST_VIDEO_FORMAT_NV21:
1565         case GST_VIDEO_FORMAT_P010_10LE:
1566         case GST_VIDEO_FORMAT_P012_LE:
1567         case GST_VIDEO_FORMAT_P016_LE:
1568           clear_color->color[0][0] = converted[0];
1569           clear_color->color[0][1] = 0;
1570           clear_color->color[0][2] = 0;
1571           clear_color->color[0][3] = 1.0;
1572           if (format == GST_VIDEO_FORMAT_NV21) {
1573             clear_color->color[1][0] = converted[2];
1574             clear_color->color[1][1] = converted[1];
1575           } else {
1576             clear_color->color[1][0] = converted[1];
1577             clear_color->color[1][1] = converted[2];
1578           }
1579           clear_color->color[1][2] = 0;
1580           clear_color->color[1][3] = 1.0;
1581           break;
1582         case GST_VIDEO_FORMAT_I420:
1583         case GST_VIDEO_FORMAT_YV12:
1584         case GST_VIDEO_FORMAT_I420_10LE:
1585         case GST_VIDEO_FORMAT_I420_12LE:
1586         case GST_VIDEO_FORMAT_Y42B:
1587         case GST_VIDEO_FORMAT_I422_10LE:
1588         case GST_VIDEO_FORMAT_I422_12LE:
1589         case GST_VIDEO_FORMAT_Y444:
1590         case GST_VIDEO_FORMAT_Y444_10LE:
1591         case GST_VIDEO_FORMAT_Y444_12LE:
1592         case GST_VIDEO_FORMAT_Y444_16LE:
1593           clear_color->color[0][0] = converted[0];
1594           clear_color->color[0][1] = 0;
1595           clear_color->color[0][2] = 0;
1596           clear_color->color[0][3] = 1.0;
1597           if (format == GST_VIDEO_FORMAT_YV12) {
1598             clear_color->color[1][0] = converted[2];
1599             clear_color->color[2][0] = converted[1];
1600           } else {
1601             clear_color->color[1][0] = converted[1];
1602             clear_color->color[2][0] = converted[2];
1603           }
1604           clear_color->color[1][1] = 0;
1605           clear_color->color[1][2] = 0;
1606           clear_color->color[1][3] = 1.0;
1607           clear_color->color[2][1] = 0;
1608           clear_color->color[2][2] = 0;
1609           clear_color->color[2][3] = 1.0;
1610           break;
1611         default:
1612           g_assert_not_reached ();
1613           break;
1614       }
1615     }
1616   }
1617 }
1618
1619 static gboolean
1620 gst_d3d11_compositor_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
1621 {
1622   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1623   GstCapsFeatures *features;
1624   GstVideoInfo info;
1625
1626   if (!gst_video_info_from_caps (&info, caps)) {
1627     GST_ERROR_OBJECT (self, "Failed to convert caps to info");
1628     return FALSE;
1629   }
1630
1631   if (self->negotiated_caps && gst_caps_is_equal (self->negotiated_caps, caps)) {
1632     GST_DEBUG_OBJECT (self, "Negotiated caps is not changed");
1633     goto done;
1634   }
1635
1636   features = gst_caps_get_features (caps, 0);
1637   if (features
1638       && gst_caps_features_contains (features,
1639           GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
1640     GST_DEBUG_OBJECT (self, "Negotiated with D3D11 memory caps");
1641     self->downstream_supports_d3d11 = TRUE;
1642   } else {
1643     GST_DEBUG_OBJECT (self, "Negotiated with system memory caps");
1644     self->downstream_supports_d3d11 = FALSE;
1645   }
1646
1647   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (self),
1648       (GstElementForeachPadFunc) gst_d3d11_compositor_pad_clear_resource,
1649       nullptr);
1650
1651   gst_clear_buffer (&self->fallback_buf);
1652   g_clear_pointer (&self->checker_background, gst_d3d11_compositor_quad_free);
1653
1654   gst_d3d11_compositor_calculate_background_color (self, &info);
1655
1656   if (!self->downstream_supports_d3d11) {
1657     GstD3D11AllocationParams *d3d11_params;
1658     GstBufferPool *pool;
1659     GstFlowReturn flow_ret;
1660
1661     d3d11_params = gst_d3d11_allocation_params_new (self->device,
1662         &info, GST_D3D11_ALLOCATION_FLAG_DEFAULT,
1663         D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, 0);
1664
1665     pool = gst_d3d11_buffer_pool_new_with_options (self->device,
1666         caps, d3d11_params, 0, 0);
1667     gst_d3d11_allocation_params_free (d3d11_params);
1668
1669     if (!pool) {
1670       GST_ERROR_OBJECT (self, "Failed to create pool");
1671       return FALSE;
1672     }
1673
1674     if (!gst_buffer_pool_set_active (pool, TRUE)) {
1675       GST_ERROR_OBJECT (self, "Failed to set active");
1676       gst_object_unref (pool);
1677       return FALSE;
1678     }
1679
1680     flow_ret = gst_buffer_pool_acquire_buffer (pool, &self->fallback_buf,
1681         nullptr);
1682     if (flow_ret != GST_FLOW_OK) {
1683       GST_ERROR_OBJECT (self, "Failed to acquire buffer");
1684       gst_object_unref (pool);
1685       return FALSE;
1686     }
1687
1688     gst_buffer_pool_set_active (pool, FALSE);
1689     gst_object_unref (pool);
1690   }
1691
1692   gst_caps_replace (&self->negotiated_caps, caps);
1693
1694 done:
1695   return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps);
1696 }
1697
1698 static gboolean
1699 gst_d3d11_compositor_propose_allocation (GstAggregator * agg,
1700     GstAggregatorPad * pad, GstQuery * decide_query, GstQuery * query)
1701 {
1702   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1703   GstVideoInfo info;
1704   GstBufferPool *pool;
1705   GstCaps *caps;
1706   guint size;
1707
1708   gst_query_parse_allocation (query, &caps, nullptr);
1709
1710   if (caps == nullptr)
1711     return FALSE;
1712
1713   if (!gst_video_info_from_caps (&info, caps))
1714     return FALSE;
1715
1716   if (gst_query_get_n_allocation_pools (query) == 0) {
1717     GstCapsFeatures *features;
1718     GstStructure *config;
1719     gboolean is_d3d11 = FALSE;
1720
1721     features = gst_caps_get_features (caps, 0);
1722     if (features
1723         && gst_caps_features_contains (features,
1724             GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
1725       GST_DEBUG_OBJECT (pad, "upstream support d3d11 memory");
1726       pool = gst_d3d11_buffer_pool_new (self->device);
1727       is_d3d11 = TRUE;
1728     } else {
1729       pool = gst_video_buffer_pool_new ();
1730     }
1731
1732     if (!pool) {
1733       GST_ERROR_OBJECT (self, "Failed to create buffer pool");
1734       return FALSE;
1735     }
1736
1737     config = gst_buffer_pool_get_config (pool);
1738     gst_buffer_pool_config_add_option (config,
1739         GST_BUFFER_POOL_OPTION_VIDEO_META);
1740
1741     size = GST_VIDEO_INFO_SIZE (&info);
1742     if (is_d3d11) {
1743       GstD3D11AllocationParams *d3d11_params;
1744
1745       d3d11_params =
1746           gst_d3d11_allocation_params_new (self->device,
1747           &info, GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_SHADER_RESOURCE,
1748           0);
1749
1750       gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
1751       gst_d3d11_allocation_params_free (d3d11_params);
1752     } else {
1753       gst_buffer_pool_config_add_option (config,
1754           GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1755     }
1756
1757     gst_buffer_pool_config_set_params (config, caps, (guint) size, 0, 0);
1758
1759     if (!gst_buffer_pool_set_config (pool, config)) {
1760       GST_ERROR_OBJECT (pool, "Couldn't set config");
1761       gst_object_unref (pool);
1762
1763       return FALSE;
1764     }
1765
1766     /* d3d11 buffer pool will update buffer size based on allocated texture,
1767      * get size from config again */
1768     config = gst_buffer_pool_get_config (pool);
1769     gst_buffer_pool_config_get_params (config,
1770         nullptr, &size, nullptr, nullptr);
1771     gst_structure_free (config);
1772
1773     gst_query_add_allocation_pool (query, pool, size, 0, 0);
1774     gst_object_unref (pool);
1775   }
1776
1777   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1778   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, nullptr);
1779
1780   return TRUE;
1781 }
1782
1783 static gboolean
1784 gst_d3d11_compositor_decide_allocation (GstAggregator * agg, GstQuery * query)
1785 {
1786   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
1787   GstCaps *caps;
1788   GstBufferPool *pool = nullptr;
1789   guint n, size, min, max;
1790   GstVideoInfo info;
1791   GstStructure *config;
1792   gboolean use_d3d11_pool;
1793
1794   gst_query_parse_allocation (query, &caps, nullptr);
1795
1796   if (!caps) {
1797     GST_DEBUG_OBJECT (self, "No output caps");
1798     return FALSE;
1799   }
1800
1801   if (!gst_video_info_from_caps (&info, caps)) {
1802     GST_ERROR_OBJECT (self, "Invalid caps");
1803     return FALSE;
1804   }
1805
1806   use_d3d11_pool = self->downstream_supports_d3d11;
1807
1808   n = gst_query_get_n_allocation_pools (query);
1809   if (n > 0)
1810     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1811
1812   /* create our own pool */
1813   if (pool && use_d3d11_pool) {
1814     if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
1815       GST_DEBUG_OBJECT (self,
1816           "Downstream pool is not d3d11, will create new one");
1817       gst_clear_object (&pool);
1818     } else {
1819       GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
1820       if (dpool->device != self->device) {
1821         GST_DEBUG_OBJECT (self, "Different device, will create new one");
1822         gst_clear_object (&pool);
1823       }
1824     }
1825   }
1826
1827   size = (guint) info.size;
1828
1829   if (!pool) {
1830     if (use_d3d11_pool)
1831       pool = gst_d3d11_buffer_pool_new (self->device);
1832     else
1833       pool = gst_video_buffer_pool_new ();
1834
1835     min = 0;
1836     max = 0;
1837   }
1838
1839   config = gst_buffer_pool_get_config (pool);
1840   gst_buffer_pool_config_set_params (config, caps, size, min, max);
1841   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1842
1843   if (use_d3d11_pool) {
1844     GstD3D11AllocationParams *d3d11_params;
1845
1846     d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
1847     if (!d3d11_params) {
1848       d3d11_params = gst_d3d11_allocation_params_new (self->device,
1849           &info, GST_D3D11_ALLOCATION_FLAG_DEFAULT, D3D11_BIND_RENDER_TARGET,
1850           0);
1851     } else {
1852       guint i;
1853
1854       for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) {
1855         d3d11_params->desc[i].BindFlags |= D3D11_BIND_RENDER_TARGET;
1856       }
1857     }
1858
1859     gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
1860     gst_d3d11_allocation_params_free (d3d11_params);
1861   }
1862
1863   gst_buffer_pool_set_config (pool, config);
1864
1865   /* d3d11 buffer pool will update buffer size based on allocated texture,
1866    * get size from config again */
1867   config = gst_buffer_pool_get_config (pool);
1868   gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
1869   gst_structure_free (config);
1870
1871   if (n > 0)
1872     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1873   else
1874     gst_query_add_allocation_pool (query, pool, size, min, max);
1875   gst_object_unref (pool);
1876
1877   return TRUE;
1878 }
1879
1880 typedef struct
1881 {
1882   struct
1883   {
1884     FLOAT x;
1885     FLOAT y;
1886     FLOAT z;
1887   } position;
1888   struct
1889   {
1890     FLOAT u;
1891     FLOAT v;
1892   } texture;
1893 } VertexData;
1894
1895 static GstD3D11CompositorQuad *
1896 gst_d3d11_compositor_create_checker_quad (GstD3D11Compositor * self,
1897     const GstVideoInfo * info)
1898 {
1899   GstD3D11CompositorQuad *quad = nullptr;
1900   VertexData *vertex_data;
1901   WORD *indices;
1902   ID3D11Device *device_handle;
1903   ID3D11DeviceContext *context_handle;
1904   D3D11_MAPPED_SUBRESOURCE map;
1905   D3D11_INPUT_ELEMENT_DESC input_desc;
1906   D3D11_BUFFER_DESC buffer_desc;
1907   ComPtr < ID3D11Buffer > vertex_buffer;
1908   ComPtr < ID3D11Buffer > index_buffer;
1909   ComPtr < ID3D11PixelShader > ps;
1910   ComPtr < ID3D11VertexShader > vs;
1911   ComPtr < ID3D11InputLayout > layout;
1912   HRESULT hr;
1913   const gchar *ps_src;
1914
1915   device_handle = gst_d3d11_device_get_device_handle (self->device);
1916   context_handle = gst_d3d11_device_get_device_context_handle (self->device);
1917
1918   if (GST_VIDEO_INFO_IS_RGB (info)) {
1919     ps_src = checker_ps_src_rgb;
1920   } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_VUYA) {
1921     ps_src = checker_ps_src_vuya;
1922   } else {
1923     ps_src = checker_ps_src_luma;
1924   }
1925
1926   hr = gst_d3d11_create_pixel_shader_simple (self->device, ps_src, "main", &ps);
1927   if (!gst_d3d11_result (hr, self->device)) {
1928     GST_ERROR_OBJECT (self, "Couldn't setup pixel shader");
1929     return nullptr;
1930   }
1931
1932   memset (&input_desc, 0, sizeof (D3D11_INPUT_ELEMENT_DESC));
1933   input_desc.SemanticName = "POSITION";
1934   input_desc.SemanticIndex = 0;
1935   input_desc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
1936   input_desc.InputSlot = 0;
1937   input_desc.AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
1938   input_desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
1939   input_desc.InstanceDataStepRate = 0;
1940
1941   hr = gst_d3d11_create_vertex_shader_simple (self->device, checker_vs_src,
1942       "main", &input_desc, 1, &vs, &layout);
1943   if (!gst_d3d11_result (hr, self->device)) {
1944     GST_ERROR_OBJECT (self, "Couldn't setup vertex shader");
1945     return nullptr;
1946   }
1947
1948   memset (&buffer_desc, 0, sizeof (D3D11_BUFFER_DESC));
1949   buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
1950   buffer_desc.ByteWidth = sizeof (VertexData) * 4;
1951   buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1952   buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1953
1954   hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &vertex_buffer);
1955   if (!gst_d3d11_result (hr, self->device)) {
1956     GST_ERROR_OBJECT (self,
1957         "Couldn't create vertex buffer, hr: 0x%x", (guint) hr);
1958     return nullptr;
1959   }
1960
1961   hr = context_handle->Map (vertex_buffer.Get (),
1962       0, D3D11_MAP_WRITE_DISCARD, 0, &map);
1963
1964   if (!gst_d3d11_result (hr, self->device)) {
1965     GST_ERROR_OBJECT (self, "Couldn't map vertex buffer, hr: 0x%x", (guint) hr);
1966     return nullptr;
1967   }
1968
1969   vertex_data = (VertexData *) map.pData;
1970   /* bottom left */
1971   vertex_data[0].position.x = -1.0f;
1972   vertex_data[0].position.y = -1.0f;
1973   vertex_data[0].position.z = 0.0f;
1974   vertex_data[0].texture.u = 0.0f;
1975   vertex_data[0].texture.v = 1.0f;
1976
1977   /* top left */
1978   vertex_data[1].position.x = -1.0f;
1979   vertex_data[1].position.y = 1.0f;
1980   vertex_data[1].position.z = 0.0f;
1981   vertex_data[1].texture.u = 0.0f;
1982   vertex_data[1].texture.v = 0.0f;
1983
1984   /* top right */
1985   vertex_data[2].position.x = 1.0f;
1986   vertex_data[2].position.y = 1.0f;
1987   vertex_data[2].position.z = 0.0f;
1988   vertex_data[2].texture.u = 1.0f;
1989   vertex_data[2].texture.v = 0.0f;
1990
1991   /* bottom right */
1992   vertex_data[3].position.x = 1.0f;
1993   vertex_data[3].position.y = -1.0f;
1994   vertex_data[3].position.z = 0.0f;
1995   vertex_data[3].texture.u = 1.0f;
1996   vertex_data[3].texture.v = 1.0f;
1997
1998   context_handle->Unmap (vertex_buffer.Get (), 0);
1999
2000   buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
2001   buffer_desc.ByteWidth = sizeof (WORD) * 6;
2002   buffer_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
2003   buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
2004
2005   hr = device_handle->CreateBuffer (&buffer_desc, nullptr, &index_buffer);
2006   if (!gst_d3d11_result (hr, self->device)) {
2007     GST_ERROR_OBJECT (self,
2008         "Couldn't create index buffer, hr: 0x%x", (guint) hr);
2009     return nullptr;
2010   }
2011
2012   hr = context_handle->Map (index_buffer.Get (),
2013       0, D3D11_MAP_WRITE_DISCARD, 0, &map);
2014
2015   if (!gst_d3d11_result (hr, self->device)) {
2016     GST_ERROR_OBJECT (self, "Couldn't map index buffer, hr: 0x%x", (guint) hr);
2017     return nullptr;
2018   }
2019
2020   indices = (WORD *) map.pData;
2021
2022   /* clockwise indexing */
2023   indices[0] = 0;               /* bottom left */
2024   indices[1] = 1;               /* top left */
2025   indices[2] = 2;               /* top right */
2026
2027   indices[3] = 3;               /* bottom right */
2028   indices[4] = 0;               /* bottom left  */
2029   indices[5] = 2;               /* top right */
2030
2031   context_handle->Unmap (index_buffer.Get (), 0);
2032   quad = g_new0 (GstD3D11CompositorQuad, 1);
2033   quad->ps = ps.Detach ();
2034   quad->vs = vs.Detach ();
2035   quad->layout = layout.Detach ();
2036   quad->vertex_buffer = vertex_buffer.Detach ();
2037   quad->index_buffer = index_buffer.Detach ();
2038
2039   quad->viewport.TopLeftX = 0;
2040   quad->viewport.TopLeftY = 0;
2041   quad->viewport.Width = GST_VIDEO_INFO_WIDTH (info);
2042   quad->viewport.Height = GST_VIDEO_INFO_HEIGHT (info);
2043   quad->viewport.MinDepth = 0.0f;
2044   quad->viewport.MaxDepth = 1.0f;
2045
2046   return quad;
2047 }
2048
2049 static void
2050 gst_d3d11_compositor_quad_free (GstD3D11CompositorQuad * quad)
2051 {
2052   if (!quad)
2053     return;
2054
2055   GST_D3D11_CLEAR_COM (quad->ps);
2056   GST_D3D11_CLEAR_COM (quad->vs);
2057   GST_D3D11_CLEAR_COM (quad->layout);
2058   GST_D3D11_CLEAR_COM (quad->vertex_buffer);
2059   GST_D3D11_CLEAR_COM (quad->index_buffer);
2060
2061   g_free (quad);
2062 }
2063
2064 static gboolean
2065 gst_d3d11_compositor_draw_background_checker (GstD3D11Compositor * self,
2066     ID3D11RenderTargetView * rtv)
2067 {
2068   ID3D11DeviceContext *context =
2069       gst_d3d11_device_get_device_context_handle (self->device);
2070   UINT offsets = 0;
2071   UINT strides = sizeof (VertexData);
2072   GstD3D11CompositorQuad *quad;
2073
2074   if (!self->checker_background) {
2075     GstVideoInfo *info = &GST_VIDEO_AGGREGATOR_CAST (self)->info;
2076
2077     self->checker_background =
2078         gst_d3d11_compositor_create_checker_quad (self, info);
2079     if (!self->checker_background)
2080       return FALSE;
2081   }
2082
2083   quad = self->checker_background;
2084   context->IASetPrimitiveTopology (D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
2085   context->IASetInputLayout (quad->layout);
2086   context->IASetVertexBuffers (0, 1, &quad->vertex_buffer, &strides, &offsets);
2087   context->IASetIndexBuffer (quad->index_buffer, DXGI_FORMAT_R16_UINT, 0);
2088   context->VSSetShader (quad->vs, nullptr, 0);
2089   context->PSSetShader (quad->ps, nullptr, 0);
2090   context->RSSetViewports (1, &quad->viewport);
2091   context->OMSetRenderTargets (1, &rtv, nullptr);
2092   context->OMSetBlendState (nullptr, nullptr, 0xffffffff);
2093   context->DrawIndexed (6, 0, 0);
2094   context->OMSetRenderTargets (0, nullptr, nullptr);
2095
2096   return TRUE;
2097 }
2098
2099 /* Must be called with d3d11 device lock */
2100 static gboolean
2101 gst_d3d11_compositor_draw_background (GstD3D11Compositor * self,
2102     ID3D11RenderTargetView * rtv[GST_VIDEO_MAX_PLANES], guint num_rtv)
2103 {
2104   ID3D11DeviceContext *context =
2105       gst_d3d11_device_get_device_context_handle (self->device);
2106   GstD3D11CompositorClearColor *color = &self->clear_color[0];
2107
2108   if (self->background == GST_D3D11_COMPOSITOR_BACKGROUND_CHECKER) {
2109     if (!gst_d3d11_compositor_draw_background_checker (self, rtv[0]))
2110       return FALSE;
2111
2112     /* clear U and V components if needed */
2113     for (guint i = 1; i < num_rtv; i++)
2114       context->ClearRenderTargetView (rtv[i], color->color[i]);
2115
2116     return TRUE;
2117   }
2118
2119   switch (self->background) {
2120     case GST_D3D11_COMPOSITOR_BACKGROUND_BLACK:
2121       color = &self->clear_color[0];
2122       break;
2123     case GST_D3D11_COMPOSITOR_BACKGROUND_WHITE:
2124       color = &self->clear_color[1];
2125       break;
2126     case GST_D3D11_COMPOSITOR_BACKGROUND_TRANSPARENT:
2127       color = &self->clear_color[2];
2128       break;
2129     default:
2130       g_assert_not_reached ();
2131       return FALSE;
2132   }
2133
2134   for (guint i = 0; i < num_rtv; i++)
2135     context->ClearRenderTargetView (rtv[i], color->color[i]);
2136
2137   return TRUE;
2138 }
2139
2140 static GstFlowReturn
2141 gst_d3d11_compositor_aggregate_frames (GstVideoAggregator * vagg,
2142     GstBuffer * outbuf)
2143 {
2144   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (vagg);
2145   GList *iter;
2146   GstBuffer *target_buf = outbuf;
2147   GstFlowReturn ret = GST_FLOW_OK;
2148   ID3D11RenderTargetView *rtv[GST_VIDEO_MAX_PLANES] = { nullptr, };
2149   GstVideoFrame target_frame;
2150   guint num_rtv = GST_VIDEO_INFO_N_PLANES (&vagg->info);
2151   GstD3D11DeviceLockGuard lk (self->device);
2152
2153   if (!self->downstream_supports_d3d11)
2154     target_buf = self->fallback_buf;
2155
2156   if (!gst_video_frame_map (&target_frame, &vagg->info, target_buf,
2157           (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
2158     GST_ERROR_OBJECT (self, "Failed to map render target frame");
2159     return GST_FLOW_ERROR;
2160   }
2161
2162   if (!gst_d3d11_buffer_get_render_target_view (target_buf, rtv)) {
2163     GST_ERROR_OBJECT (self, "RTV is unavailable");
2164     gst_video_frame_unmap (&target_frame);
2165     return GST_FLOW_ERROR;
2166   }
2167
2168   if (!gst_d3d11_compositor_draw_background (self, rtv, num_rtv)) {
2169     GST_ERROR_OBJECT (self, "Couldn't draw background");
2170     gst_video_frame_unmap (&target_frame);
2171     return GST_FLOW_ERROR;
2172   }
2173
2174   gst_video_frame_unmap (&target_frame);
2175
2176   GST_OBJECT_LOCK (self);
2177   for (iter = GST_ELEMENT (vagg)->sinkpads; iter; iter = g_list_next (iter)) {
2178     GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (iter->data);
2179     GstD3D11CompositorPad *cpad = GST_D3D11_COMPOSITOR_PAD (pad);
2180     GstVideoFrame *prepared_frame =
2181         gst_video_aggregator_pad_get_prepared_frame (pad);
2182     gint x, y, w, h;
2183     GstVideoCropMeta *crop_meta;
2184
2185     if (!prepared_frame)
2186       continue;
2187
2188     if (!gst_d3d11_compositor_pad_setup_converter (pad, vagg)) {
2189       GST_ERROR_OBJECT (self, "Couldn't setup converter");
2190       ret = GST_FLOW_ERROR;
2191       break;
2192     }
2193
2194     crop_meta = gst_buffer_get_video_crop_meta (prepared_frame->buffer);
2195     if (crop_meta) {
2196       x = crop_meta->x;
2197       y = crop_meta->y;
2198       w = crop_meta->width;
2199       h = crop_meta->height;
2200     } else {
2201       x = y = 0;
2202       w = pad->info.width;
2203       h = pad->info.height;
2204     }
2205
2206     g_object_set (cpad->convert, "src-x", x, "src-y", y, "src-width", w,
2207         "src-height", h, nullptr);
2208
2209     if (!gst_d3d11_converter_convert_buffer_unlocked (cpad->convert,
2210             prepared_frame->buffer, target_buf)) {
2211       GST_ERROR_OBJECT (self, "Couldn't convert frame");
2212       ret = GST_FLOW_ERROR;
2213       break;
2214     }
2215   }
2216   GST_OBJECT_UNLOCK (self);
2217
2218   if (ret != GST_FLOW_OK)
2219     return ret;
2220
2221   if (!self->downstream_supports_d3d11) {
2222     if (!gst_d3d11_buffer_copy_into (outbuf, self->fallback_buf, &vagg->info)) {
2223       GST_ERROR_OBJECT (self, "Couldn't copy input buffer to fallback buffer");
2224       return GST_FLOW_ERROR;
2225     }
2226   }
2227
2228   return GST_FLOW_OK;
2229 }
2230
2231 typedef struct
2232 {
2233   /* without holding ref */
2234   GstD3D11Device *other_device;
2235   gboolean have_same_device;
2236 } DeviceCheckData;
2237
2238 static gboolean
2239 gst_d3d11_compositor_check_device_update (GstElement * agg,
2240     GstVideoAggregatorPad * vpad, DeviceCheckData * data)
2241 {
2242   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (agg);
2243   GstBuffer *buf;
2244   GstMemory *mem;
2245   GstD3D11Memory *dmem;
2246   gboolean update_device = FALSE;
2247
2248   buf = gst_video_aggregator_pad_get_current_buffer (vpad);
2249   if (!buf)
2250     return TRUE;
2251
2252   /* Ignore gap buffer */
2253   if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP) ||
2254       gst_buffer_get_size (buf) == 0) {
2255     return TRUE;
2256   }
2257
2258   mem = gst_buffer_peek_memory (buf, 0);
2259   if (!gst_is_d3d11_memory (mem))
2260     return TRUE;
2261
2262   dmem = GST_D3D11_MEMORY_CAST (mem);
2263
2264   /* We can use existing device */
2265   if (dmem->device == self->device) {
2266     data->have_same_device = TRUE;
2267     return FALSE;
2268   }
2269
2270   if (self->adapter < 0) {
2271     update_device = TRUE;
2272   } else {
2273     guint adapter = 0;
2274
2275     g_object_get (dmem->device, "adapter", &adapter, nullptr);
2276     /* The same GPU as what user wanted, update */
2277     if (adapter == (guint) self->adapter)
2278       update_device = TRUE;
2279   }
2280
2281   if (!update_device)
2282     return TRUE;
2283
2284   data->other_device = dmem->device;
2285
2286   /* Keep iterate since there might be one buffer which holds the same device
2287    * as ours */
2288   return TRUE;
2289 }
2290
2291 static GstFlowReturn
2292 gst_d3d11_compositor_create_output_buffer (GstVideoAggregator * vagg,
2293     GstBuffer ** outbuffer)
2294 {
2295   GstD3D11Compositor *self = GST_D3D11_COMPOSITOR (vagg);
2296   DeviceCheckData data;
2297
2298   /* Check whether there is at least one sinkpad which holds d3d11 buffer
2299    * with compatible device, and if not, update our device */
2300   data.other_device = nullptr;
2301   data.have_same_device = FALSE;
2302
2303   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg),
2304       (GstElementForeachPadFunc) gst_d3d11_compositor_check_device_update,
2305       &data);
2306
2307   if (data.have_same_device || !data.other_device) {
2308     return
2309         GST_VIDEO_AGGREGATOR_CLASS (parent_class)->create_output_buffer (vagg,
2310         outbuffer);
2311   }
2312
2313   /* Clear all device dependent resources */
2314   gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg),
2315       (GstElementForeachPadFunc) gst_d3d11_compositor_pad_clear_resource,
2316       nullptr);
2317
2318   gst_clear_buffer (&self->fallback_buf);
2319   g_clear_pointer (&self->checker_background, gst_d3d11_compositor_quad_free);
2320
2321   GST_INFO_OBJECT (self, "Updating device %" GST_PTR_FORMAT " -> %"
2322       GST_PTR_FORMAT, self->device, data.other_device);
2323   gst_object_unref (self->device);
2324   self->device = (GstD3D11Device *) gst_object_ref (data.other_device);
2325
2326   /* We cannot call gst_aggregator_negotiate() here, since GstVideoAggregator
2327    * is holding GST_VIDEO_AGGREGATOR_LOCK() already.
2328    * Mark reconfigure and do reconfigure later */
2329   gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg));
2330
2331   return GST_AGGREGATOR_FLOW_NEED_DATA;
2332 }