• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-d3d11screencapturesrc
23  * @title: d3d11screencapturesrc
24  *
25  * A DXGI Desktop Duplication API based screen capture element
26  *
27  * ## Example launch line
28  * ```
29  * gst-launch-1.0 d3d11screencapturesrc ! queue ! d3d11videosink
30  * ```
31  *
32  * Since: 1.20
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include "gstd3d11screencapturesrc.h"
40 #include "gstd3d11screencapture.h"
41 #include "gstd3d11pluginutils.h"
42 #include "gstd3d11shader.h"
43 #include <wrl.h>
44 #include <string.h>
45 
46 /* *INDENT-OFF* */
47 using namespace Microsoft::WRL;
48 /* *INDENT-ON* */
49 
50 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_screen_capture_debug);
51 #define GST_CAT_DEFAULT gst_d3d11_screen_capture_debug
52 
53 enum
54 {
55   PROP_0,
56   PROP_MONITOR_INDEX,
57   PROP_MONITOR_HANDLE,
58   PROP_SHOW_CURSOR,
59 
60   PROP_LAST,
61 };
62 
63 static GParamSpec *properties[PROP_LAST];
64 
65 #define DEFAULT_MONITOR_INDEX -1
66 #define DEFAULT_SHOW_CURSOR FALSE
67 
68 static GstStaticCaps template_caps =
69     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
70     (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, "BGRA") ";"
71     GST_VIDEO_CAPS_MAKE ("BGRA"));
72 
73 struct _GstD3D11ScreenCaptureSrc
74 {
75   GstBaseSrc src;
76 
77   guint64 last_frame_no;
78   GstClockID clock_id;
79   GstVideoInfo video_info;
80 
81   GstD3D11Device *device;
82   GstD3D11ScreenCapture *capture;
83 
84   GstBufferPool *pool;
85 
86   gint64 adapter_luid;
87   gint monitor_index;
88   HMONITOR monitor_handle;
89   gboolean show_cursor;
90 
91   gboolean flushing;
92   GstClockTime min_latency;
93   GstClockTime max_latency;
94 
95   gboolean downstream_supports_d3d11;
96 
97   ID3D11VertexShader *vs;
98   ID3D11PixelShader *ps;
99   ID3D11InputLayout *layout;
100   ID3D11SamplerState *sampler;
101   ID3D11BlendState *blend;
102 };
103 
104 static void gst_d3d11_screen_capture_src_dispose (GObject * object);
105 static void gst_d3d11_screen_capture_src_set_property (GObject * object,
106     guint prop_id, const GValue * value, GParamSpec * pspec);
107 static void gst_d3d11_screen_capture_src_get_property (GObject * object,
108     guint prop_id, GValue * value, GParamSpec * pspec);
109 
110 static void gst_d3d11_screen_capture_src_set_context (GstElement * element,
111     GstContext * context);
112 
113 static GstCaps *gst_d3d11_screen_capture_src_get_caps (GstBaseSrc * bsrc,
114     GstCaps * filter);
115 static GstCaps *gst_d3d11_screen_capture_src_fixate (GstBaseSrc * bsrc,
116     GstCaps * caps);
117 static gboolean gst_d3d11_screen_capture_src_set_caps (GstBaseSrc * bsrc,
118     GstCaps * caps);
119 static gboolean gst_d3d11_screen_capture_src_decide_allocation (GstBaseSrc *
120     bsrc, GstQuery * query);
121 static gboolean gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc);
122 static gboolean gst_d3d11_screen_capture_src_stop (GstBaseSrc * bsrc);
123 static gboolean gst_d3d11_screen_capture_src_unlock (GstBaseSrc * bsrc);
124 static gboolean gst_d3d11_screen_capture_src_unlock_stop (GstBaseSrc * bsrc);
125 static gboolean
126 gst_d3d11_screen_capture_src_src_query (GstBaseSrc * bsrc, GstQuery * query);
127 
128 static GstFlowReturn gst_d3d11_screen_capture_src_create (GstBaseSrc * bsrc,
129     guint64 offset, guint size, GstBuffer ** buf);
130 
131 #define gst_d3d11_screen_capture_src_parent_class parent_class
132 G_DEFINE_TYPE (GstD3D11ScreenCaptureSrc, gst_d3d11_screen_capture_src,
133     GST_TYPE_BASE_SRC);
134 
135 static void
gst_d3d11_screen_capture_src_class_init(GstD3D11ScreenCaptureSrcClass * klass)136 gst_d3d11_screen_capture_src_class_init (GstD3D11ScreenCaptureSrcClass * klass)
137 {
138   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
139   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
140   GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
141   GstCaps *caps;
142 
143   gobject_class->dispose = gst_d3d11_screen_capture_src_dispose;
144   gobject_class->set_property = gst_d3d11_screen_capture_src_set_property;
145   gobject_class->get_property = gst_d3d11_screen_capture_src_get_property;
146 
147   properties[PROP_MONITOR_INDEX] =
148       g_param_spec_int ("monitor-index", "Monitor Index",
149       "Zero-based index for monitor to capture (-1 = primary monitor)",
150       -1, G_MAXINT, DEFAULT_MONITOR_INDEX,
151       (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
152           G_PARAM_STATIC_STRINGS));
153 
154   properties[PROP_MONITOR_HANDLE] =
155       g_param_spec_uint64 ("monitor-handle", "Monitor Handle",
156       "A HMONITOR handle of monitor to capture",
157       0, G_MAXUINT64, 0,
158       (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
159           G_PARAM_STATIC_STRINGS));
160 
161   properties[PROP_SHOW_CURSOR] =
162       g_param_spec_boolean ("show-cursor",
163       "Show Mouse Cursor", "Whether to show mouse cursor",
164       DEFAULT_SHOW_CURSOR,
165       (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
166 
167   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
168 
169   element_class->set_context =
170       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_set_context);
171 
172   gst_element_class_set_static_metadata (element_class,
173       "Direct3D11 screen capture src", "Source/Video",
174       "Capture desktop image by using Desktop Duplication API",
175       "Seungha Yang <seungha@centricular.com>");
176 
177   caps = gst_d3d11_get_updated_template_caps (&template_caps);
178   gst_element_class_add_pad_template (element_class,
179       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps));
180   gst_caps_unref (caps);
181 
182   basesrc_class->get_caps =
183       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_get_caps);
184   basesrc_class->fixate =
185       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_fixate);
186   basesrc_class->set_caps =
187       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_set_caps);
188   basesrc_class->decide_allocation =
189       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_decide_allocation);
190   basesrc_class->start = GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_start);
191   basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_stop);
192   basesrc_class->unlock =
193       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_unlock);
194   basesrc_class->unlock_stop =
195       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_unlock_stop);
196   basesrc_class->query =
197       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_src_query);
198 
199   basesrc_class->create =
200       GST_DEBUG_FUNCPTR (gst_d3d11_screen_capture_src_create);
201 }
202 
203 static void
gst_d3d11_screen_capture_src_init(GstD3D11ScreenCaptureSrc * self)204 gst_d3d11_screen_capture_src_init (GstD3D11ScreenCaptureSrc * self)
205 {
206   gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
207   gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
208 
209   self->monitor_index = DEFAULT_MONITOR_INDEX;
210   self->show_cursor = DEFAULT_SHOW_CURSOR;
211   self->min_latency = GST_CLOCK_TIME_NONE;
212   self->max_latency = GST_CLOCK_TIME_NONE;
213 }
214 
215 static void
gst_d3d11_screen_capture_src_dispose(GObject * object)216 gst_d3d11_screen_capture_src_dispose (GObject * object)
217 {
218   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (object);
219 
220   gst_clear_object (&self->capture);
221   gst_clear_object (&self->device);
222 
223   G_OBJECT_CLASS (parent_class)->dispose (object);
224 }
225 
226 static void
gst_d3d11_screen_capture_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)227 gst_d3d11_screen_capture_src_set_property (GObject * object, guint prop_id,
228     const GValue * value, GParamSpec * pspec)
229 {
230   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (object);
231 
232   switch (prop_id) {
233     case PROP_MONITOR_INDEX:
234       self->monitor_index = g_value_get_int (value);
235       break;
236     case PROP_MONITOR_HANDLE:
237       self->monitor_handle = (HMONITOR) g_value_get_uint64 (value);
238       break;
239     case PROP_SHOW_CURSOR:
240       self->show_cursor = g_value_get_boolean (value);
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245   };
246 }
247 
248 static void
gst_d3d11_screen_capture_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)249 gst_d3d11_screen_capture_src_get_property (GObject * object, guint prop_id,
250     GValue * value, GParamSpec * pspec)
251 {
252   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (object);
253 
254   switch (prop_id) {
255     case PROP_MONITOR_INDEX:
256       g_value_set_int (value, self->monitor_index);
257       break;
258     case PROP_MONITOR_HANDLE:
259       g_value_set_uint64 (value, (guint64) self->monitor_handle);
260       break;
261     case PROP_SHOW_CURSOR:
262       g_value_set_boolean (value, self->show_cursor);
263       break;
264     default:
265       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266       break;
267   };
268 }
269 
270 static void
gst_d3d11_screen_capture_src_set_context(GstElement * element,GstContext * context)271 gst_d3d11_screen_capture_src_set_context (GstElement * element,
272     GstContext * context)
273 {
274   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (element);
275 
276   gst_d3d11_handle_set_context_for_adapter_luid (element,
277       context, self->adapter_luid, &self->device);
278 
279   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
280 }
281 
282 static GstCaps *
gst_d3d11_screen_capture_src_get_caps(GstBaseSrc * bsrc,GstCaps * filter)283 gst_d3d11_screen_capture_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
284 {
285   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
286   GstCaps *caps = NULL;
287   guint width, height;
288 
289   if (!self->capture) {
290     GST_DEBUG_OBJECT (self, "Duplication object is not configured yet");
291     return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
292   }
293 
294   if (!gst_d3d11_screen_capture_get_size (self->capture, &width, &height)) {
295     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
296         ("Cannot query supported resolution"), (NULL));
297     return NULL;
298   }
299 
300   caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
301   caps = gst_caps_make_writable (caps);
302 
303   gst_caps_set_simple (caps, "width", G_TYPE_INT, width, "height",
304       G_TYPE_INT, height, nullptr);
305 
306   if (filter) {
307     GstCaps *tmp =
308         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
309     gst_caps_unref (caps);
310 
311     caps = tmp;
312   }
313 
314   return caps;
315 }
316 
317 static GstCaps *
gst_d3d11_screen_capture_src_fixate(GstBaseSrc * bsrc,GstCaps * caps)318 gst_d3d11_screen_capture_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
319 {
320   guint size;
321   GstCaps *d3d11_caps = nullptr;
322 
323   caps = gst_caps_make_writable (caps);
324   size = gst_caps_get_size (caps);
325 
326   for (guint i = 0; i < size; i++) {
327     GstStructure *s;
328 
329     s = gst_caps_get_structure (caps, i);
330     gst_structure_fixate_field_nearest_fraction (s, "framerate", 30, 1);
331 
332     if (!d3d11_caps) {
333       GstCapsFeatures *features;
334       features = gst_caps_get_features (caps, i);
335 
336       if (features && gst_caps_features_contains (features,
337               GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
338 
339         d3d11_caps = gst_caps_new_empty ();
340         gst_caps_append_structure (d3d11_caps, gst_structure_copy (s));
341 
342         gst_caps_set_features (d3d11_caps, 0,
343             gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY,
344                 nullptr));
345 
346         break;
347       }
348     }
349   }
350 
351   if (d3d11_caps) {
352     gst_caps_unref (caps);
353     caps = d3d11_caps;
354   }
355 
356   return gst_caps_fixate (caps);
357 }
358 
359 static gboolean
gst_d3d11_screen_capture_src_set_caps(GstBaseSrc * bsrc,GstCaps * caps)360 gst_d3d11_screen_capture_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
361 {
362   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
363   GstCapsFeatures *features;
364 
365   GST_DEBUG_OBJECT (self, "Set caps %" GST_PTR_FORMAT, caps);
366 
367   features = gst_caps_get_features (caps, 0);
368   if (features && gst_caps_features_contains (features,
369           GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
370     self->downstream_supports_d3d11 = TRUE;
371   } else {
372     self->downstream_supports_d3d11 = FALSE;
373   }
374 
375   gst_video_info_from_caps (&self->video_info, caps);
376   gst_base_src_set_blocksize (bsrc, GST_VIDEO_INFO_SIZE (&self->video_info));
377 
378   return TRUE;
379 }
380 
381 static gboolean
gst_d3d11_screen_capture_src_decide_allocation(GstBaseSrc * bsrc,GstQuery * query)382 gst_d3d11_screen_capture_src_decide_allocation (GstBaseSrc * bsrc,
383     GstQuery * query)
384 {
385   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
386   GstBufferPool *pool = NULL;
387   GstStructure *config;
388   GstD3D11AllocationParams *d3d11_params;
389   GstCaps *caps;
390   guint min, max, size;
391   gboolean update_pool;
392   GstVideoInfo vinfo;
393 
394   if (self->pool) {
395     gst_buffer_pool_set_active (self->pool, FALSE);
396     gst_clear_object (&self->pool);
397   }
398 
399   gst_query_parse_allocation (query, &caps, NULL);
400 
401   if (!caps) {
402     GST_ERROR_OBJECT (self, "No output caps");
403     return FALSE;
404   }
405 
406   gst_video_info_from_caps (&vinfo, caps);
407 
408   if (gst_query_get_n_allocation_pools (query) > 0) {
409     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
410     update_pool = TRUE;
411   } else {
412     size = GST_VIDEO_INFO_SIZE (&vinfo);
413 
414     min = max = 0;
415     update_pool = FALSE;
416   }
417 
418   if (pool && self->downstream_supports_d3d11) {
419     if (!GST_IS_D3D11_BUFFER_POOL (pool)) {
420       gst_clear_object (&pool);
421     } else {
422       GstD3D11BufferPool *dpool = GST_D3D11_BUFFER_POOL (pool);
423       if (dpool->device != self->device)
424         gst_clear_object (&pool);
425     }
426   }
427 
428   if (!pool) {
429     if (self->downstream_supports_d3d11)
430       pool = gst_d3d11_buffer_pool_new (self->device);
431     else
432       pool = gst_video_buffer_pool_new ();
433   }
434 
435   config = gst_buffer_pool_get_config (pool);
436 
437   gst_buffer_pool_config_set_params (config, caps, size, min, max);
438   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
439 
440   if (self->downstream_supports_d3d11) {
441     d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
442     if (!d3d11_params) {
443       d3d11_params = gst_d3d11_allocation_params_new (self->device, &vinfo,
444           (GstD3D11AllocationFlags) 0,
445           D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
446     } else {
447       d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
448     }
449 
450     gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
451     gst_d3d11_allocation_params_free (d3d11_params);
452   }
453 
454   if (!gst_buffer_pool_set_config (pool, config)) {
455     GST_ERROR_OBJECT (self, "Failed to set config");
456     goto error;
457   }
458 
459   if (self->downstream_supports_d3d11) {
460     /* d3d11 buffer pool will update buffer size based on allocated texture,
461      * get size from config again */
462     config = gst_buffer_pool_get_config (pool);
463     gst_buffer_pool_config_get_params (config,
464         nullptr, &size, nullptr, nullptr);
465     gst_structure_free (config);
466   } else {
467     self->pool = gst_d3d11_buffer_pool_new (self->device);
468 
469     config = gst_buffer_pool_get_config (self->pool);
470 
471     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
472     gst_buffer_pool_config_add_option (config,
473         GST_BUFFER_POOL_OPTION_VIDEO_META);
474 
475     d3d11_params = gst_buffer_pool_config_get_d3d11_allocation_params (config);
476     if (!d3d11_params) {
477       d3d11_params = gst_d3d11_allocation_params_new (self->device, &vinfo,
478           (GstD3D11AllocationFlags) 0, D3D11_BIND_RENDER_TARGET);
479     } else {
480       d3d11_params->desc[0].BindFlags |= D3D11_BIND_RENDER_TARGET;
481     }
482 
483     gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
484     gst_d3d11_allocation_params_free (d3d11_params);
485 
486     if (!gst_buffer_pool_set_config (self->pool, config)) {
487       GST_ERROR_OBJECT (self, "Failed to set config for internal pool");
488       goto error;
489     }
490 
491     if (!gst_buffer_pool_set_active (self->pool, TRUE)) {
492       GST_ERROR_OBJECT (self, "Failed to activate internal pool");
493       goto error;
494     }
495   }
496 
497   if (update_pool)
498     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
499   else
500     gst_query_add_allocation_pool (query, pool, size, min, max);
501 
502   gst_object_unref (pool);
503 
504   return TRUE;
505 
506 error:
507   gst_clear_object (&self->pool);
508   gst_clear_object (&pool);
509 
510   return FALSE;
511 }
512 
513 static gboolean
gst_d3d11_screen_capture_prepare_shader(GstD3D11ScreenCaptureSrc * self)514 gst_d3d11_screen_capture_prepare_shader (GstD3D11ScreenCaptureSrc * self)
515 {
516   /* *INDENT-OFF* */
517   static const gchar vs_str[] =
518       "struct VS_INPUT {\n"
519       "  float4 Position: POSITION;\n"
520       "  float2 Texture: TEXCOORD;\n"
521       "};\n"
522       "\n"
523       "struct VS_OUTPUT {\n"
524       "  float4 Position: SV_POSITION;\n"
525       "  float2 Texture: TEXCOORD;\n"
526       "};\n"
527       "\n"
528       "VS_OUTPUT main (VS_INPUT input)\n"
529       "{\n"
530       "  return input;\n"
531       "}";
532   static const gchar ps_str[] =
533       "Texture2D shaderTexture;\n"
534       "SamplerState samplerState;\n"
535       "\n"
536       "struct PS_INPUT {\n"
537       "  float4 Position: SV_POSITION;\n"
538       "  float2 Texture: TEXCOORD;\n"
539       "};\n"
540       "\n"
541       "struct PS_OUTPUT {\n"
542       "  float4 Plane: SV_Target;\n"
543       "};\n"
544       "\n"
545       "PS_OUTPUT main(PS_INPUT input)\n"
546       "{\n"
547       "  PS_OUTPUT output;\n"
548       "  output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
549       "  return output;\n"
550       "}";
551   /* *INDENT-ON* */
552   D3D11_INPUT_ELEMENT_DESC input_desc[] = {
553     {"POSITION",
554         0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
555     {"TEXCOORD",
556         0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
557   };
558   ComPtr < ID3D11VertexShader > vs;
559   ComPtr < ID3D11InputLayout > layout;
560   ComPtr < ID3D11PixelShader > ps;
561   ComPtr < ID3D11SamplerState > sampler;
562   ComPtr < ID3D11BlendState > blend;
563   D3D11_SAMPLER_DESC sampler_desc;
564   D3D11_BLEND_DESC blend_desc;
565   ID3D11Device *device_handle;
566   HRESULT hr;
567 
568   device_handle = gst_d3d11_device_get_device_handle (self->device);
569 
570   if (!gst_d3d11_create_vertex_shader (self->device,
571           vs_str, input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
572     GST_ERROR_OBJECT (self, "Failed to create vertex shader");
573     return FALSE;
574   }
575 
576   if (!gst_d3d11_create_pixel_shader (self->device, ps_str, &ps)) {
577     GST_ERROR_OBJECT (self, "Failed to create pixel shader");
578     return FALSE;
579   }
580 
581   memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
582   sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
583   sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
584   sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
585   sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
586   sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
587   sampler_desc.MinLOD = 0;
588   sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
589 
590   hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
591   if (!gst_d3d11_result (hr, self->device)) {
592     GST_ERROR_OBJECT (self,
593         "Failed to create sampler state, hr 0x%x", (guint) hr);
594     return FALSE;
595   }
596 
597   blend_desc.AlphaToCoverageEnable = FALSE;
598   blend_desc.IndependentBlendEnable = FALSE;
599   blend_desc.RenderTarget[0].BlendEnable = TRUE;
600   blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
601   blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
602   blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
603   blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
604   blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
605   blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
606   blend_desc.RenderTarget[0].RenderTargetWriteMask =
607       D3D11_COLOR_WRITE_ENABLE_ALL;
608 
609   hr = device_handle->CreateBlendState (&blend_desc, &blend);
610   if (!gst_d3d11_result (hr, self->device)) {
611     GST_ERROR_OBJECT (self,
612         "Failed to create blend state, hr 0x%x", (guint) hr);
613     return FALSE;
614   }
615 
616   self->vs = vs.Detach ();
617   self->ps = ps.Detach ();
618   self->layout = layout.Detach ();
619   self->sampler = sampler.Detach ();
620   self->blend = blend.Detach ();
621 
622   return TRUE;
623 }
624 
625 static gboolean
gst_d3d11_screen_capture_src_start(GstBaseSrc * bsrc)626 gst_d3d11_screen_capture_src_start (GstBaseSrc * bsrc)
627 {
628   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
629   GstFlowReturn ret;
630   HMONITOR monitor = self->monitor_handle;
631   ComPtr < IDXGIAdapter1 > adapter;
632   DXGI_ADAPTER_DESC desc;
633   HRESULT hr;
634 
635   if (monitor) {
636     hr = gst_d3d11_screen_capture_find_output_for_monitor (monitor,
637         &adapter, nullptr);
638   } else if (self->monitor_index < 0) {
639     hr = gst_d3d11_screen_capture_find_primary_monitor (&monitor,
640         &adapter, nullptr);
641   } else {
642     hr = gst_d3d11_screen_capture_find_nth_monitor (self->monitor_index,
643         &monitor, &adapter, nullptr);
644   }
645 
646   if (FAILED (hr))
647     goto error;
648 
649   hr = adapter->GetDesc (&desc);
650   if (FAILED (hr))
651     goto error;
652 
653   self->adapter_luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
654   gst_clear_object (&self->device);
655 
656   if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT_CAST (self),
657           self->adapter_luid, &self->device)) {
658     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
659         ("D3D11 device for LUID %" G_GINT64_FORMAT " is unavailble",
660             self->adapter_luid), (nullptr));
661 
662     return FALSE;
663   }
664 
665   self->capture = gst_d3d11_screen_capture_new (self->device, monitor);
666   if (!self->capture)
667     goto error;
668 
669   /* Check if we can open device */
670   ret = gst_d3d11_screen_capture_prepare (self->capture);
671   switch (ret) {
672     case GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR:
673     case GST_FLOW_OK:
674       break;
675     case GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED:
676       goto unsupported;
677     default:
678       goto error;
679   }
680 
681   if (!gst_d3d11_screen_capture_prepare_shader (self))
682     goto error;
683 
684   self->last_frame_no = -1;
685   self->min_latency = self->max_latency = GST_CLOCK_TIME_NONE;
686 
687   return TRUE;
688 
689 error:
690   {
691     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
692         ("Failed to prepare capture object with given configuration, "
693             "monitor-index: %d, monitor-handle: %p",
694             self->monitor_index, self->monitor_handle), (nullptr));
695     return FALSE;
696   }
697 
698 unsupported:
699   {
700     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
701         ("Failed to prepare capture object with given configuration, "
702             "monitor-index: %d, monitor-handle: %p",
703             self->monitor_index, self->monitor_handle),
704         ("Try run the application on the integrated GPU"));
705     return FALSE;
706   }
707 }
708 
709 static gboolean
gst_d3d11_screen_capture_src_stop(GstBaseSrc * bsrc)710 gst_d3d11_screen_capture_src_stop (GstBaseSrc * bsrc)
711 {
712   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
713 
714   if (self->pool) {
715     gst_buffer_pool_set_active (self->pool, FALSE);
716     gst_clear_object (&self->pool);
717   }
718 
719   GST_D3D11_CLEAR_COM (self->vs);
720   GST_D3D11_CLEAR_COM (self->ps);
721   GST_D3D11_CLEAR_COM (self->layout);
722   GST_D3D11_CLEAR_COM (self->sampler);
723   GST_D3D11_CLEAR_COM (self->blend);
724 
725   gst_clear_object (&self->capture);
726   gst_clear_object (&self->device);
727 
728   return TRUE;
729 }
730 
731 static gboolean
gst_d3d11_screen_capture_src_unlock(GstBaseSrc * bsrc)732 gst_d3d11_screen_capture_src_unlock (GstBaseSrc * bsrc)
733 {
734   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
735 
736   GST_OBJECT_LOCK (self);
737   if (self->clock_id) {
738     GST_DEBUG_OBJECT (self, "Waking up waiting clock");
739     gst_clock_id_unschedule (self->clock_id);
740   }
741   self->flushing = TRUE;
742   GST_OBJECT_UNLOCK (self);
743 
744   return TRUE;
745 }
746 
747 static gboolean
gst_d3d11_screen_capture_src_unlock_stop(GstBaseSrc * bsrc)748 gst_d3d11_screen_capture_src_unlock_stop (GstBaseSrc * bsrc)
749 {
750   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
751 
752   GST_OBJECT_LOCK (self);
753   self->flushing = FALSE;
754   GST_OBJECT_UNLOCK (self);
755 
756   return TRUE;
757 }
758 
759 static gboolean
gst_d3d11_screen_capture_src_src_query(GstBaseSrc * bsrc,GstQuery * query)760 gst_d3d11_screen_capture_src_src_query (GstBaseSrc * bsrc, GstQuery * query)
761 {
762   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
763 
764   switch (GST_QUERY_TYPE (query)) {
765     case GST_QUERY_CONTEXT:
766       if (gst_d3d11_handle_context_query (GST_ELEMENT_CAST (self), query,
767               self->device)) {
768         return TRUE;
769       }
770       break;
771     case GST_QUERY_LATENCY:
772       if (GST_CLOCK_TIME_IS_VALID (self->min_latency)) {
773         gst_query_set_latency (query,
774             TRUE, self->min_latency, self->max_latency);
775         return TRUE;
776       }
777       break;
778     default:
779       break;
780   }
781 
782   return GST_BASE_SRC_CLASS (parent_class)->query (bsrc, query);
783 }
784 
785 static GstFlowReturn
gst_d3d11_screen_capture_src_create(GstBaseSrc * bsrc,guint64 offset,guint size,GstBuffer ** buf)786 gst_d3d11_screen_capture_src_create (GstBaseSrc * bsrc, guint64 offset,
787     guint size, GstBuffer ** buf)
788 {
789   GstD3D11ScreenCaptureSrc *self = GST_D3D11_SCREEN_CAPTURE_SRC (bsrc);
790   ID3D11Texture2D *texture;
791   ID3D11RenderTargetView *rtv = NULL;
792   gint fps_n, fps_d;
793   GstMapInfo info;
794   GstMemory *mem;
795   GstD3D11Memory *dmem;
796   GstFlowReturn ret = GST_FLOW_OK;
797   GstClock *clock = NULL;
798   GstClockTime base_time;
799   GstClockTime next_capture_ts;
800   GstClockTime before_capture;
801   GstClockTime after_capture;
802   GstClockTime latency;
803   GstClockTime dur;
804   gboolean update_latency = FALSE;
805   guint64 next_frame_no;
806   gboolean draw_mouse;
807   /* Just magic number... */
808   gint unsupported_retry_count = 100;
809   GstBuffer *buffer = NULL;
810   GstBuffer *sysmem_buf = NULL;
811 
812   if (!self->capture) {
813     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ,
814         ("Couldn't configure capture object"), (nullptr));
815     return GST_FLOW_NOT_NEGOTIATED;
816   }
817 
818   fps_n = GST_VIDEO_INFO_FPS_N (&self->video_info);
819   fps_d = GST_VIDEO_INFO_FPS_D (&self->video_info);
820 
821   if (fps_n <= 0 || fps_d <= 0)
822     return GST_FLOW_NOT_NEGOTIATED;
823 
824 again:
825   clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
826   if (!clock) {
827     GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
828         ("Cannot operate without a clock"), (nullptr));
829     return GST_FLOW_ERROR;
830   }
831 
832   /* Check flushing before waiting clock because we are might be doing
833    * retry */
834   GST_OBJECT_LOCK (self);
835   if (self->flushing) {
836     ret = GST_FLOW_FLUSHING;
837     GST_OBJECT_UNLOCK (self);
838     goto out;
839   }
840 
841   base_time = GST_ELEMENT_CAST (self)->base_time;
842   next_capture_ts = gst_clock_get_time (clock);
843   next_capture_ts -= base_time;
844 
845   next_frame_no = gst_util_uint64_scale (next_capture_ts,
846       fps_n, GST_SECOND * fps_d);
847 
848   if (next_frame_no == self->last_frame_no) {
849     GstClockID id;
850     GstClockReturn clock_ret;
851 
852     /* Need to wait for the next frame */
853     next_frame_no += 1;
854 
855     /* Figure out what the next frame time is */
856     next_capture_ts = gst_util_uint64_scale (next_frame_no,
857         fps_d * GST_SECOND, fps_n);
858 
859     id = gst_clock_new_single_shot_id (GST_ELEMENT_CLOCK (self),
860         next_capture_ts + base_time);
861     self->clock_id = id;
862 
863     /* release the object lock while waiting */
864     GST_OBJECT_UNLOCK (self);
865 
866     GST_LOG_OBJECT (self, "Waiting for next frame time %" GST_TIME_FORMAT,
867         GST_TIME_ARGS (next_capture_ts));
868     clock_ret = gst_clock_id_wait (id, NULL);
869     GST_OBJECT_LOCK (self);
870 
871     gst_clock_id_unref (id);
872     self->clock_id = NULL;
873 
874     if (clock_ret == GST_CLOCK_UNSCHEDULED) {
875       /* Got woken up by the unlock function */
876       ret = GST_FLOW_FLUSHING;
877       GST_OBJECT_UNLOCK (self);
878       goto out;
879     }
880     /* Duration is a complete 1/fps frame duration */
881     dur = gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
882   } else {
883     GstClockTime next_frame_ts;
884 
885     GST_LOG_OBJECT (self, "No need to wait for next frame time %"
886         GST_TIME_FORMAT " next frame = %" G_GINT64_FORMAT " prev = %"
887         G_GINT64_FORMAT, GST_TIME_ARGS (next_capture_ts), next_frame_no,
888         self->last_frame_no);
889 
890     next_frame_ts = gst_util_uint64_scale (next_frame_no + 1,
891         fps_d * GST_SECOND, fps_n);
892     /* Frame duration is from now until the next expected capture time */
893     dur = next_frame_ts - next_capture_ts;
894   }
895 
896   self->last_frame_no = next_frame_no;
897   GST_OBJECT_UNLOCK (self);
898 
899   if (!buffer) {
900     if (self->downstream_supports_d3d11) {
901       ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc,
902           offset, size, &buffer);
903     } else {
904       if (!self->pool) {
905         GST_ERROR_OBJECT (self, "Internal pool wasn't configured");
906         goto error;
907       }
908 
909       ret = gst_buffer_pool_acquire_buffer (self->pool, &buffer, nullptr);
910     }
911 
912     if (ret != GST_FLOW_OK)
913       goto out;
914   }
915 
916   mem = gst_buffer_peek_memory (buffer, 0);
917   if (!gst_is_d3d11_memory (mem)) {
918     GST_ERROR_OBJECT (self, "Not a D3D11 memory");
919     goto error;
920   }
921 
922   dmem = (GstD3D11Memory *) mem;
923   draw_mouse = self->show_cursor;
924   rtv = gst_d3d11_memory_get_render_target_view (dmem, 0);
925   if (draw_mouse && !rtv) {
926     GST_ERROR_OBJECT (self, "Render target view is unavailable");
927     goto error;
928   }
929 
930   if (!gst_memory_map (mem, &info,
931           (GstMapFlags) (GST_MAP_WRITE | GST_MAP_D3D11))) {
932     GST_ERROR_OBJECT (self, "Failed to map d3d11 memory");
933     goto error;
934   }
935 
936   texture = (ID3D11Texture2D *) info.data;
937   before_capture = gst_clock_get_time (clock);
938   ret = gst_d3d11_screen_capture_do_capture (self->capture, self->device,
939       texture, rtv, self->vs, self->ps, self->layout, self->sampler,
940       self->blend, draw_mouse);
941   gst_memory_unmap (mem, &info);
942 
943   switch (ret) {
944     case GST_D3D11_SCREEN_CAPTURE_FLOW_EXPECTED_ERROR:
945       GST_WARNING_OBJECT (self, "Got expected error, try again");
946       gst_clear_object (&clock);
947       goto again;
948     case GST_D3D11_SCREEN_CAPTURE_FLOW_UNSUPPORTED:
949       GST_WARNING_OBJECT (self, "Got DXGI_ERROR_UNSUPPORTED error");
950       unsupported_retry_count--;
951 
952       if (unsupported_retry_count < 0)
953         goto error;
954 
955       gst_clear_object (&clock);
956       goto again;
957     case GST_D3D11_SCREEN_CAPTURE_FLOW_SIZE_CHANGED:
958       GST_INFO_OBJECT (self, "Size was changed, need negotiation");
959       gst_clear_buffer (&buffer);
960       gst_clear_object (&clock);
961 
962       if (!gst_base_src_negotiate (bsrc)) {
963         GST_ERROR_OBJECT (self, "Failed to negotiate with new size");
964         ret = GST_FLOW_NOT_NEGOTIATED;
965         goto out;
966       }
967       goto again;
968     default:
969       break;
970   }
971 
972   if (!self->downstream_supports_d3d11) {
973     GstVideoFrame src_frame, dst_frame;
974     gboolean copy_ret;
975 
976     ret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc,
977         offset, size, &sysmem_buf);
978     if (ret != GST_FLOW_OK) {
979       gst_clear_buffer (&buffer);
980       goto out;
981     }
982 
983     if (!gst_video_frame_map (&src_frame, &self->video_info, buffer,
984             GST_MAP_READ)) {
985       GST_ERROR_OBJECT (self, "Failed to map d3d11 buffer");
986       goto error;
987     }
988 
989     if (!gst_video_frame_map (&dst_frame, &self->video_info, sysmem_buf,
990             GST_MAP_WRITE)) {
991       GST_ERROR_OBJECT (self, "Failed to map sysmem buffer");
992       gst_video_frame_unmap (&src_frame);
993       goto error;
994     }
995 
996     copy_ret = gst_video_frame_copy (&dst_frame, &src_frame);
997     gst_video_frame_unmap (&dst_frame);
998     gst_video_frame_unmap (&src_frame);
999 
1000     if (!copy_ret) {
1001       GST_ERROR_OBJECT (self, "Failed to copy frame");
1002       goto error;
1003     }
1004 
1005     gst_buffer_unref (buffer);
1006     buffer = sysmem_buf;
1007     sysmem_buf = nullptr;
1008   }
1009 
1010   GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
1011   GST_BUFFER_PTS (buffer) = next_capture_ts;
1012   GST_BUFFER_DURATION (buffer) = dur;
1013 
1014   after_capture = gst_clock_get_time (clock);
1015   latency = after_capture - before_capture;
1016   if (!GST_CLOCK_TIME_IS_VALID (self->min_latency)) {
1017     self->min_latency = self->max_latency = latency;
1018     update_latency = TRUE;
1019     GST_DEBUG_OBJECT (self, "Initial latency %" GST_TIME_FORMAT,
1020         GST_TIME_ARGS (latency));
1021   }
1022 
1023   if (latency > self->max_latency) {
1024     self->max_latency = latency;
1025     update_latency = TRUE;
1026     GST_DEBUG_OBJECT (self, "Updating max latency %" GST_TIME_FORMAT,
1027         GST_TIME_ARGS (latency));
1028   }
1029 
1030   if (update_latency) {
1031     gst_element_post_message (GST_ELEMENT_CAST (self),
1032         gst_message_new_latency (GST_OBJECT_CAST (self)));
1033   }
1034 
1035 out:
1036   gst_clear_object (&clock);
1037   *buf = buffer;
1038 
1039   return ret;
1040 
1041 error:
1042   gst_clear_buffer (&buffer);
1043   gst_clear_buffer (&sysmem_buf);
1044   gst_clear_object (&clock);
1045 
1046   return GST_FLOW_ERROR;
1047 }
1048