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