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