3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
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.
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.
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.
22 * The MIT License (MIT)
24 * Copyright (c) Microsoft Corporation
26 * Permission is hereby granted, free of charge, to any person obtaining a copy
27 * of this software and associated documentation files (the "Software"), to deal
28 * in the Software without restriction, including without limitation the rights
29 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 * copies of the Software, and to permit persons to whom the Software is
31 * furnished to do so, subject to the following conditions:
33 * The above copyright notice and this permission notice shall be included in
34 * all copies or substantial portions of the Software.
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49 #include "gstd3d11desktopdup.h"
50 #include "gstd3d11shader.h"
51 #include "gstd3d11pluginutils.h"
57 using namespace Microsoft::WRL;
61 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_desktop_dup_debug);
62 #define GST_CAT_DEFAULT gst_d3d11_desktop_dup_debug
66 /* List of GstD3D11DesktopDup weakref */
67 G_LOCK_DEFINE_STATIC (dupl_list_lock);
68 static GList *dupl_list = nullptr;
77 #define DEFAULT_MONITOR_INDEX -1
79 /* Below implemenation were taken from Microsoft sample
80 * https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/DXGIDesktopDuplication
85 /* Define our own MyFLOAT3 and MyFLOAT2 struct, since MinGW doesn't support
96 MyFLOAT3(const MyFLOAT3&) = default;
97 MyFLOAT3& operator=(const MyFLOAT3&) = default;
99 MyFLOAT3(MyFLOAT3&&) = default;
100 MyFLOAT3& operator=(MyFLOAT3&&) = default;
102 constexpr MyFLOAT3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
103 explicit MyFLOAT3(const float *pArray) : x(pArray[0]), y(pArray[1]), z(pArray[2]) {}
111 MyFLOAT2() = default;
113 MyFLOAT2(const MyFLOAT2&) = default;
114 MyFLOAT2& operator=(const MyFLOAT2&) = default;
116 MyFLOAT2(MyFLOAT2&&) = default;
117 MyFLOAT2& operator=(MyFLOAT2&&) = default;
119 constexpr MyFLOAT2(float _x, float _y) : x(_x), y(_y) {}
120 explicit MyFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {}
129 /* List of expected error cases */
130 /* These are the errors we expect from general Dxgi API due to a transition */
131 HRESULT SystemTransitionsExpectedErrors[] = {
132 DXGI_ERROR_DEVICE_REMOVED,
133 DXGI_ERROR_ACCESS_LOST,
134 static_cast<HRESULT>(WAIT_ABANDONED),
138 /* These are the errors we expect from IDXGIOutput1::DuplicateOutput
139 * due to a transition */
140 HRESULT CreateDuplicationExpectedErrors[] = {
141 DXGI_ERROR_DEVICE_REMOVED,
142 static_cast<HRESULT>(E_ACCESSDENIED),
143 DXGI_ERROR_UNSUPPORTED,
144 DXGI_ERROR_SESSION_DISCONNECTED,
148 /* These are the errors we expect from IDXGIOutputDuplication methods
149 * due to a transition */
150 HRESULT FrameInfoExpectedErrors[] = {
151 DXGI_ERROR_DEVICE_REMOVED,
152 DXGI_ERROR_ACCESS_LOST,
156 /* These are the errors we expect from IDXGIAdapter::EnumOutputs methods
157 * due to outputs becoming stale during a transition */
158 HRESULT EnumOutputsExpectedErrors[] = {
159 DXGI_ERROR_NOT_FOUND,
164 gst_d3d11_desktop_dup_return_from_hr (ID3D11Device * device,
165 HRESULT hr, HRESULT * expected_errors = nullptr)
167 HRESULT translated_hr = hr;
169 /* On an error check if the DX device is lost */
171 HRESULT remove_reason = device->GetDeviceRemovedReason ();
173 switch (remove_reason) {
174 case DXGI_ERROR_DEVICE_REMOVED:
175 case DXGI_ERROR_DEVICE_RESET:
176 case static_cast<HRESULT>(E_OUTOFMEMORY):
177 /* Our device has been stopped due to an external event on the GPU so
178 * map them all to device removed and continue processing the condition
180 translated_hr = DXGI_ERROR_DEVICE_REMOVED;
183 /* Device is not removed so use original error */
186 /* Device is removed but not a error we want to remap */
187 translated_hr = remove_reason;
192 /* Check if this error was expected or not */
193 if (expected_errors) {
194 HRESULT* rst = expected_errors;
196 while (*rst != S_OK) {
197 if (*rst == translated_hr)
198 return GST_D3D11_DESKTOP_DUP_FLOW_EXPECTED_ERROR;
204 return GST_FLOW_ERROR;
211 : PtrShapeBuffer (nullptr)
214 LastTimeStamp.QuadPart = 0;
220 delete[] PtrShapeBuffer;
224 MaybeReallocBuffer (UINT buffer_size)
226 if (buffer_size <= BufferSize)
230 delete[] PtrShapeBuffer;
232 PtrShapeBuffer = new BYTE[buffer_size];
233 BufferSize = buffer_size;
236 BYTE* PtrShapeBuffer;
238 DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
241 LARGE_INTEGER LastTimeStamp;
244 class D3D11DesktopDupObject
247 D3D11DesktopDupObject ()
249 , metadata_buffer_(nullptr)
250 , metadata_buffer_size_(0)
251 , vertex_buffer_(nullptr)
252 , vertex_buffer_size_(0)
256 ~D3D11DesktopDupObject ()
258 if (metadata_buffer_)
259 delete[] metadata_buffer_;
262 delete[] vertex_buffer_;
264 gst_clear_object (&device_);
268 Init (GstD3D11Device * device, ID3D11Texture2D * texture, UINT monitor_index)
272 if (!InitShader (device))
273 return GST_FLOW_ERROR;
275 ret = InitDupl (device, monitor_index);
276 if (ret != GST_FLOW_OK)
279 GST_INFO ("Init done");
281 shared_texture_ = texture;
282 device_ = (GstD3D11Device *) gst_object_ref (device);
288 Capture (gboolean draw_mouse)
291 bool timeout = false;
292 ComPtr<ID3D11Texture2D> texture;
293 UINT move_count, dirty_count;
294 DXGI_OUTDUPL_FRAME_INFO frame_info;
296 GST_TRACE ("Capturing");
297 ret = GetFrame (&texture, &move_count, &dirty_count, &frame_info, &timeout);
298 if (ret != GST_FLOW_OK)
301 /* Nothing updated */
303 GST_TRACE ("timeout");
308 GST_TRACE ("Getting mouse pointer info");
309 ret = GetMouse (&ptr_info_, &frame_info);
310 if (ret != GST_FLOW_OK) {
311 GST_WARNING ("Couldn't get mouse pointer info");
312 dupl_->ReleaseFrame ();
317 ret = ProcessFrame (texture.Get(), shared_texture_.Get(),
318 &output_desc_, move_count, dirty_count, &frame_info);
320 if (ret != GST_FLOW_OK) {
321 dupl_->ReleaseFrame ();
322 GST_WARNING ("Couldn't process frame");
326 HRESULT hr = dupl_->ReleaseFrame ();
327 if (!gst_d3d11_result (hr, device_)) {
328 GST_WARNING ("Couldn't release frame");
329 return gst_d3d11_desktop_dup_return_from_hr (nullptr, hr, FrameInfoExpectedErrors);
332 GST_TRACE ("Capture done");
337 bool DrawMouse (ID3D11RenderTargetView * rtv)
339 GST_TRACE ("Drawing mouse");
341 if (!ptr_info_.Visible) {
342 GST_TRACE ("Mouse is invisiable");
346 ComPtr<ID3D11Texture2D> MouseTex;
347 ComPtr<ID3D11ShaderResourceView> ShaderRes;
348 ComPtr<ID3D11Buffer> VertexBufferMouse;
349 D3D11_SUBRESOURCE_DATA InitData;
350 D3D11_TEXTURE2D_DESC Desc;
351 D3D11_SHADER_RESOURCE_VIEW_DESC SDesc;
352 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
353 ID3D11DeviceContext *context_handle =
354 gst_d3d11_device_get_device_context_handle (device_);
356 VERTEX Vertices[NUMVERTICES] =
358 {MyFLOAT3(-1.0f, -1.0f, 0), MyFLOAT2(0.0f, 1.0f)},
359 {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
360 {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
361 {MyFLOAT3(1.0f, -1.0f, 0), MyFLOAT2(1.0f, 1.0f)},
362 {MyFLOAT3(-1.0f, 1.0f, 0), MyFLOAT2(0.0f, 0.0f)},
363 {MyFLOAT3(1.0f, 1.0f, 0), MyFLOAT2(1.0f, 0.0f)},
366 D3D11_TEXTURE2D_DESC FullDesc;
367 shared_texture_->GetDesc(&FullDesc);
368 INT DesktopWidth = FullDesc.Width;
369 INT DesktopHeight = FullDesc.Height;
371 INT CenterX = (DesktopWidth / 2);
372 INT CenterY = (DesktopHeight / 2);
379 BYTE* InitBuffer = nullptr;
387 Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
388 Desc.SampleDesc.Count = 1;
389 Desc.SampleDesc.Quality = 0;
390 Desc.Usage = D3D11_USAGE_DEFAULT;
391 Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
392 Desc.CPUAccessFlags = 0;
395 // Set shader resource properties
396 SDesc.Format = Desc.Format;
397 SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
398 SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1;
399 SDesc.Texture2D.MipLevels = Desc.MipLevels;
401 switch (ptr_info_.shape_info.Type) {
402 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
403 PtrLeft = ptr_info_.Position.x;
404 PtrTop = ptr_info_.Position.y;
406 PtrWidth = static_cast<INT>(ptr_info_.shape_info.Width);
407 PtrHeight = static_cast<INT>(ptr_info_.shape_info.Height);
409 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
410 ProcessMonoMask(true, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
411 &PtrTop, &InitBuffer, &Box);
413 case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
414 ProcessMonoMask(false, &ptr_info_, &PtrWidth, &PtrHeight, &PtrLeft,
415 &PtrTop, &InitBuffer, &Box);
422 if (PtrWidth == 0 || PtrHeight == 0) {
429 Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
430 Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
431 Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX;
432 Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
433 Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
434 Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY;
435 Vertices[3].Pos.x = Vertices[2].Pos.x;
436 Vertices[3].Pos.y = Vertices[2].Pos.y;
437 Vertices[4].Pos.x = Vertices[1].Pos.x;
438 Vertices[4].Pos.y = Vertices[1].Pos.y;
439 Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX;
440 Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY;
442 Desc.Width = PtrWidth;
443 Desc.Height = PtrHeight;
446 (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
447 ptr_info_.PtrShapeBuffer : InitBuffer;
448 InitData.SysMemPitch =
449 (ptr_info_.shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ?
450 ptr_info_.shape_info.Pitch : PtrWidth * BPP;
451 InitData.SysMemSlicePitch = 0;
453 // Create mouseshape as texture
454 HRESULT hr = device_handle->CreateTexture2D(&Desc, &InitData, &MouseTex);
455 if (!gst_d3d11_result (hr, device_)) {
456 GST_ERROR ("Failed to create texture for rendering mouse");
460 // Create shader resource from texture
461 hr = device_handle->CreateShaderResourceView(MouseTex.Get(), &SDesc,
463 if (!gst_d3d11_result (hr, device_)) {
464 GST_ERROR ("Failed to create shader resource view for rendering mouse");
468 D3D11_BUFFER_DESC BDesc;
469 memset (&BDesc, 0, sizeof(D3D11_BUFFER_DESC));
470 BDesc.Usage = D3D11_USAGE_DEFAULT;
471 BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
472 BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
473 BDesc.CPUAccessFlags = 0;
475 memset (&InitData, 0, sizeof(D3D11_SUBRESOURCE_DATA));
476 InitData.pSysMem = Vertices;
478 // Create vertex buffer
479 hr = device_handle->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse);
480 if (!gst_d3d11_result (hr, device_)) {
481 GST_ERROR ("Failed to create vertex buffer for rendering mouse");
485 FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f};
486 UINT Stride = sizeof(VERTEX);
488 ID3D11SamplerState *samplers = sampler_.Get();
489 ID3D11ShaderResourceView *srv = ShaderRes.Get();
490 ID3D11Buffer *vert_buf = VertexBufferMouse.Get();
492 context_handle->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
493 context_handle->OMSetBlendState(blend_.Get(), BlendFactor, 0xFFFFFFFF);
494 context_handle->OMSetRenderTargets(1, &rtv, nullptr);
495 context_handle->VSSetShader(vs_.Get(), nullptr, 0);
496 context_handle->PSSetShader(ps_.Get(), nullptr, 0);
497 context_handle->PSSetShaderResources(0, 1, &srv);
498 context_handle->PSSetSamplers(0, 1, &samplers);
499 context_handle->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
500 context_handle->IASetInputLayout(layout_.Get());
502 context_handle->Draw(NUMVERTICES, 0);
504 /* Unbind srv and rtv from context */
506 context_handle->PSSetShaderResources (0, 1, &srv);
507 context_handle->OMSetRenderTargets (0, nullptr, nullptr);
516 /* This method is not expected to be failed unless un-recoverable error case */
518 InitShader (GstD3D11Device * device)
520 static const gchar vs_str[] =
521 "struct VS_INPUT {\n"
522 " float4 Position: POSITION;\n"
523 " float2 Texture: TEXCOORD;\n"
526 "struct VS_OUTPUT {\n"
527 " float4 Position: SV_POSITION;\n"
528 " float2 Texture: TEXCOORD;\n"
531 "VS_OUTPUT main (VS_INPUT input)\n"
536 static const gchar ps_str[] =
537 "Texture2D shaderTexture;\n"
538 "SamplerState samplerState;\n"
540 "struct PS_INPUT {\n"
541 " float4 Position: SV_POSITION;\n"
542 " float2 Texture: TEXCOORD;\n"
545 "struct PS_OUTPUT {\n"
546 " float4 Plane: SV_Target;\n"
549 "PS_OUTPUT main(PS_INPUT input)\n"
551 " PS_OUTPUT output;\n"
552 " output.Plane = shaderTexture.Sample(samplerState, input.Texture);\n"
556 D3D11_INPUT_ELEMENT_DESC input_desc[] = {
558 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
560 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
563 ComPtr<ID3D11VertexShader> vs;
564 ComPtr<ID3D11InputLayout> layout;
565 if (!gst_d3d11_create_vertex_shader (device,
566 vs_str, input_desc, G_N_ELEMENTS (input_desc), &vs, &layout)) {
567 GST_ERROR ("Failed to create vertex shader");
571 ComPtr<ID3D11PixelShader> ps;
572 if (!gst_d3d11_create_pixel_shader (device, ps_str, &ps)) {
573 GST_ERROR ("Failed to create pixel shader");
577 D3D11_SAMPLER_DESC sampler_desc;
578 memset (&sampler_desc, 0, sizeof (D3D11_SAMPLER_DESC));
579 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
580 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
581 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
582 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
583 sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
584 sampler_desc.MinLOD = 0;
585 sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
587 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
588 ComPtr<ID3D11SamplerState> sampler;
589 HRESULT hr = device_handle->CreateSamplerState (&sampler_desc, &sampler);
590 if (!gst_d3d11_result (hr, device)) {
591 GST_ERROR ("Failed to create sampler state, hr 0x%x", (guint) hr);
595 /* For blending mouse pointer texture */
596 D3D11_BLEND_DESC blend_desc;
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;
609 ComPtr<ID3D11BlendState> blend;
610 hr = device_handle->CreateBlendState (&blend_desc, &blend);
611 if (!gst_d3d11_result (hr, device)) {
612 GST_ERROR ("Failed to create blend state, hr 0x%x", (guint) hr);
616 /* Everything is prepared now */
626 /* Maybe returning expected error code depending on desktop status */
628 InitDupl (GstD3D11Device * device, UINT monitor_index)
630 ComPtr<ID3D11Device> d3d11_device;
631 ComPtr<IDXGIDevice> dxgi_device;
632 ComPtr<IDXGIAdapter> adapter;
633 ComPtr<IDXGIOutput> output;
634 ComPtr<IDXGIOutput1> output1;
636 d3d11_device = gst_d3d11_device_get_device_handle (device);
638 HRESULT hr = d3d11_device.As (&dxgi_device);
639 if (!gst_d3d11_result (hr, device)) {
640 GST_ERROR ("Couldn't get IDXGIDevice interface, hr 0x%x", (guint) hr);
641 return GST_FLOW_ERROR;
644 hr = dxgi_device->GetParent (IID_PPV_ARGS (&adapter));
645 if (!gst_d3d11_result (hr, device)) {
646 return gst_d3d11_desktop_dup_return_from_hr (d3d11_device.Get(),
647 hr, SystemTransitionsExpectedErrors);
650 hr = adapter->EnumOutputs(monitor_index, &output);
651 if (!gst_d3d11_result (hr, device)) {
652 return gst_d3d11_desktop_dup_return_from_hr (d3d11_device.Get(),
653 hr, EnumOutputsExpectedErrors);
656 output->GetDesc(&output_desc_);
658 hr = output.As (&output1);
659 if (!gst_d3d11_result (hr, device)) {
660 GST_ERROR ("Couldn't get IDXGIOutput1 interface, hr 0x%x", (guint) hr);
661 return GST_FLOW_ERROR;
664 HDESK hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
666 if (!SetThreadDesktop (hdesk)) {
667 GST_WARNING ("SetThreadDesktop() failed, error %lu", GetLastError());
670 CloseDesktop (hdesk);
672 GST_WARNING ("OpenInputDesktop() failed, error %lu", GetLastError());
675 /* FIXME: Use DuplicateOutput1 to avoid potentail color conversion */
676 hr = output1->DuplicateOutput(d3d11_device.Get(), &dupl_);
677 if (!gst_d3d11_result (hr, device)) {
678 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
679 GST_ERROR ("Hit the max allowed number of Desktop Duplication session");
680 return GST_FLOW_ERROR;
683 return gst_d3d11_desktop_dup_return_from_hr (d3d11_device.Get(), hr,
684 CreateDuplicationExpectedErrors);
691 GetMouse (PTR_INFO * ptr_info, DXGI_OUTDUPL_FRAME_INFO * frame_info)
693 /* A non-zero mouse update timestamp indicates that there is a mouse
694 * position update and optionally a shape change */
695 if (frame_info->LastMouseUpdateTime.QuadPart == 0)
698 ptr_info->Position.x = frame_info->PointerPosition.Position.x;
699 ptr_info->Position.y = frame_info->PointerPosition.Position.y;
700 ptr_info->LastTimeStamp = frame_info->LastMouseUpdateTime;
701 ptr_info->Visible = frame_info->PointerPosition.Visible != 0;
703 /* Mouse is invisible */
704 if (!ptr_info->Visible)
708 if (frame_info->PointerShapeBufferSize == 0)
711 /* Realloc buffer if needed */
712 ptr_info->MaybeReallocBuffer (frame_info->PointerShapeBufferSize);
716 HRESULT hr = dupl_->GetFramePointerShape(frame_info->PointerShapeBufferSize,
717 (void *) ptr_info->PtrShapeBuffer, &dummy, &ptr_info->shape_info);
719 if (!gst_d3d11_result (hr, device_)) {
720 ID3D11Device *device_handle =
721 gst_d3d11_device_get_device_handle (device_);
723 return gst_d3d11_desktop_dup_return_from_hr(device_handle, hr,
724 FrameInfoExpectedErrors);
731 MaybeReallocMetadataBuffer (UINT buffer_size)
733 if (buffer_size <= metadata_buffer_size_)
736 if (metadata_buffer_)
737 delete[] metadata_buffer_;
739 metadata_buffer_ = new BYTE[buffer_size];
740 metadata_buffer_size_ = buffer_size;
744 GetFrame (ID3D11Texture2D ** texture, UINT * move_count, UINT * dirty_count,
745 DXGI_OUTDUPL_FRAME_INFO * frame_info, bool* timeout)
747 ComPtr<IDXGIResource> resource;
748 ComPtr<ID3D11Texture2D> acquired_texture;
749 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
752 HRESULT hr = dupl_->AcquireNextFrame(0, frame_info, &resource);
753 if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
754 GST_TRACE ("Timeout");
764 if (!gst_d3d11_result (hr, device_)) {
765 return gst_d3d11_desktop_dup_return_from_hr(device_handle, hr,
766 FrameInfoExpectedErrors);
770 "LastPresentTime: %" G_GINT64_FORMAT
771 ", LastMouseUpdateTime: %" G_GINT64_FORMAT
772 ", AccumulatedFrames: %d"
773 ", RectsCoalesced: %d"
774 ", ProtectedContentMaskedOut: %d"
775 ", PointerPosition: (%ldx%ld, visible %d)"
776 ", TotalMetadataBufferSize: %d"
777 ", PointerShapeBufferSize: %d",
778 frame_info->LastPresentTime.QuadPart,
779 frame_info->LastMouseUpdateTime.QuadPart,
780 frame_info->AccumulatedFrames,
781 frame_info->RectsCoalesced,
782 frame_info->ProtectedContentMaskedOut,
783 frame_info->PointerPosition.Position.x,
784 frame_info->PointerPosition.Position.y,
785 frame_info->PointerPosition.Visible,
786 frame_info->TotalMetadataBufferSize,
787 frame_info->PointerShapeBufferSize);
789 hr = resource.As (&acquired_texture);
790 if (!gst_d3d11_result (hr, device_)) {
791 GST_ERROR ("Failed to get ID3D11Texture2D interface from IDXGIResource "
792 "hr 0x%x", (guint) hr);
793 return GST_FLOW_ERROR;
797 if (frame_info->TotalMetadataBufferSize) {
798 UINT buf_size = frame_info->TotalMetadataBufferSize;
800 MaybeReallocMetadataBuffer (buf_size);
802 /* Get move rectangles */
803 hr = dupl_->GetFrameMoveRects(buf_size,
804 (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_, &buf_size);
805 if (!gst_d3d11_result (hr, device_)) {
806 GST_ERROR ("Couldn't get move rect, hr 0x%x", (guint) hr);
808 return gst_d3d11_desktop_dup_return_from_hr(nullptr, hr,
809 FrameInfoExpectedErrors);
812 *move_count = buf_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
814 GST_TRACE ("MoveRects count %d", *move_count);
815 #ifndef GST_DISABLE_GST_DEBUG
817 DXGI_OUTDUPL_MOVE_RECT *rects =
818 (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_;
819 for (guint i = 0; i < *move_count; i++) {
820 GST_TRACE ("MoveRect[%d] SourcePoint: %ldx%ld, "
821 "DestinationRect (left:top:right:bottom): %ldx%ldx%ldx%ld",
822 i, rects->SourcePoint.x, rects->SourcePoint.y,
823 rects->DestinationRect.left, rects->DestinationRect.top,
824 rects->DestinationRect.right, rects->DestinationRect.bottom);
829 BYTE* dirty_rects = metadata_buffer_ + buf_size;
830 buf_size = frame_info->TotalMetadataBufferSize - buf_size;
832 /* Get dirty rectangles */
833 hr = dupl_->GetFrameDirtyRects(buf_size, (RECT *) dirty_rects, &buf_size);
834 if (!gst_d3d11_result (hr, device_)) {
835 GST_ERROR ("Couldn't get dirty rect, hr 0x%x", (guint) hr);
839 return gst_d3d11_desktop_dup_return_from_hr(nullptr,
840 hr, FrameInfoExpectedErrors);
843 *dirty_count = buf_size / sizeof(RECT);
845 GST_TRACE ("DirtyRects count %d", *dirty_count);
846 #ifndef GST_DISABLE_GST_DEBUG
848 RECT *rects = (RECT *) dirty_rects;
849 for (guint i = 0; i < *dirty_count; i++) {
850 GST_TRACE ("DirtyRect[%d] left:top:right:bottom: %ldx%ldx%ldx%ld",
851 i, rects->left, rects->top, rects->right, rects->bottom);
857 *texture = acquired_texture.Detach();
863 SetMoveRect (RECT* SrcRect, RECT* DestRect, DXGI_OUTPUT_DESC* DeskDesc,
864 DXGI_OUTDUPL_MOVE_RECT* MoveRect, INT TexWidth, INT TexHeight)
866 switch (DeskDesc->Rotation)
868 case DXGI_MODE_ROTATION_UNSPECIFIED:
869 case DXGI_MODE_ROTATION_IDENTITY:
870 SrcRect->left = MoveRect->SourcePoint.x;
871 SrcRect->top = MoveRect->SourcePoint.y;
872 SrcRect->right = MoveRect->SourcePoint.x +
873 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
874 SrcRect->bottom = MoveRect->SourcePoint.y +
875 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
877 *DestRect = MoveRect->DestinationRect;
879 case DXGI_MODE_ROTATION_ROTATE90:
880 SrcRect->left = TexHeight - (MoveRect->SourcePoint.y +
881 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
882 SrcRect->top = MoveRect->SourcePoint.x;
883 SrcRect->right = TexHeight - MoveRect->SourcePoint.y;
884 SrcRect->bottom = MoveRect->SourcePoint.x +
885 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left;
887 DestRect->left = TexHeight - MoveRect->DestinationRect.bottom;
888 DestRect->top = MoveRect->DestinationRect.left;
889 DestRect->right = TexHeight - MoveRect->DestinationRect.top;
890 DestRect->bottom = MoveRect->DestinationRect.right;
892 case DXGI_MODE_ROTATION_ROTATE180:
893 SrcRect->left = TexWidth - (MoveRect->SourcePoint.x +
894 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
895 SrcRect->top = TexHeight - (MoveRect->SourcePoint.y +
896 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top);
897 SrcRect->right = TexWidth - MoveRect->SourcePoint.x;
898 SrcRect->bottom = TexHeight - MoveRect->SourcePoint.y;
900 DestRect->left = TexWidth - MoveRect->DestinationRect.right;
901 DestRect->top = TexHeight - MoveRect->DestinationRect.bottom;
902 DestRect->right = TexWidth - MoveRect->DestinationRect.left;
903 DestRect->bottom = TexHeight - MoveRect->DestinationRect.top;
905 case DXGI_MODE_ROTATION_ROTATE270:
906 SrcRect->left = MoveRect->SourcePoint.x;
907 SrcRect->top = TexWidth - (MoveRect->SourcePoint.x +
908 MoveRect->DestinationRect.right - MoveRect->DestinationRect.left);
909 SrcRect->right = MoveRect->SourcePoint.y +
910 MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top;
911 SrcRect->bottom = TexWidth - MoveRect->SourcePoint.x;
913 DestRect->left = MoveRect->DestinationRect.top;
914 DestRect->top = TexWidth - MoveRect->DestinationRect.right;
915 DestRect->right = MoveRect->DestinationRect.bottom;
916 DestRect->bottom = TexWidth - MoveRect->DestinationRect.left;
919 memset (DestRect, 0, sizeof (RECT));
920 memset (SrcRect, 0, sizeof (RECT));
926 CopyMove (ID3D11Texture2D* SharedSurf, DXGI_OUTDUPL_MOVE_RECT* MoveBuffer,
927 UINT MoveCount,DXGI_OUTPUT_DESC* DeskDesc)
929 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
930 ID3D11DeviceContext *device_context =
931 gst_d3d11_device_get_device_context_handle (device_);
932 D3D11_TEXTURE2D_DESC FullDesc;
933 SharedSurf->GetDesc(&FullDesc);
935 GST_TRACE ("Copying MoveRects (count %d)", MoveCount);
937 /* Make new intermediate surface to copy into for moving */
938 if (!move_texture_) {
939 D3D11_TEXTURE2D_DESC MoveDesc;
941 MoveDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
942 MoveDesc.MiscFlags = 0;
943 HRESULT hr = device_handle->CreateTexture2D(&MoveDesc,
944 nullptr, &move_texture_);
945 if (!gst_d3d11_result (hr, device_)) {
946 GST_ERROR ("Couldn't create intermediate texture, hr 0x%x", (guint) hr);
947 return GST_FLOW_ERROR;
951 for (UINT i = 0; i < MoveCount; i++) {
955 SetMoveRect(&SrcRect, &DestRect, DeskDesc, &MoveBuffer[i],
956 FullDesc.Width, FullDesc.Height);
958 /* Copy rect out of shared surface */
960 Box.left = SrcRect.left;
961 Box.top = SrcRect.top;
963 Box.right = SrcRect.right;
964 Box.bottom = SrcRect.bottom;
966 device_context->CopySubresourceRegion(move_texture_.Get(),
967 0, SrcRect.left, SrcRect.top, 0, SharedSurf, 0, &Box);
969 /* Copy back to shared surface */
970 device_context->CopySubresourceRegion(SharedSurf,
971 0, DestRect.left, DestRect.top, 0, move_texture_.Get(), 0, &Box);
978 SetDirtyVert (VERTEX* Vertices, RECT* Dirty,
979 DXGI_OUTPUT_DESC* DeskDesc, D3D11_TEXTURE2D_DESC* FullDesc,
980 D3D11_TEXTURE2D_DESC* ThisDesc)
982 INT CenterX = FullDesc->Width / 2;
983 INT CenterY = FullDesc->Height / 2;
985 INT Width = FullDesc->Width;
986 INT Height = FullDesc->Height;
988 /* Rotation compensated destination rect */
989 RECT DestDirty = *Dirty;
991 /* Set appropriate coordinates compensated for rotation */
992 switch (DeskDesc->Rotation)
994 case DXGI_MODE_ROTATION_ROTATE90:
995 DestDirty.left = Width - Dirty->bottom;
996 DestDirty.top = Dirty->left;
997 DestDirty.right = Width - Dirty->top;
998 DestDirty.bottom = Dirty->right;
1000 Vertices[0].TexCoord =
1001 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1002 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1003 Vertices[1].TexCoord =
1004 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1005 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1006 Vertices[2].TexCoord =
1007 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1008 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1009 Vertices[5].TexCoord =
1010 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1011 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1013 case DXGI_MODE_ROTATION_ROTATE180:
1014 DestDirty.left = Width - Dirty->right;
1015 DestDirty.top = Height - Dirty->bottom;
1016 DestDirty.right = Width - Dirty->left;
1017 DestDirty.bottom = Height - Dirty->top;
1019 Vertices[0].TexCoord =
1020 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1021 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1022 Vertices[1].TexCoord =
1023 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1024 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1025 Vertices[2].TexCoord =
1026 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1027 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1028 Vertices[5].TexCoord =
1029 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1030 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1032 case DXGI_MODE_ROTATION_ROTATE270:
1033 DestDirty.left = Dirty->top;
1034 DestDirty.top = Height - Dirty->right;
1035 DestDirty.right = Dirty->bottom;
1036 DestDirty.bottom = Height - Dirty->left;
1038 Vertices[0].TexCoord =
1039 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1040 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1041 Vertices[1].TexCoord =
1042 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1043 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1044 Vertices[2].TexCoord =
1045 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1046 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1047 Vertices[5].TexCoord =
1048 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1049 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1051 case DXGI_MODE_ROTATION_UNSPECIFIED:
1052 case DXGI_MODE_ROTATION_IDENTITY:
1054 Vertices[0].TexCoord =
1055 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1056 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1057 Vertices[1].TexCoord =
1058 MyFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width),
1059 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1060 Vertices[2].TexCoord =
1061 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1062 Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height));
1063 Vertices[5].TexCoord =
1064 MyFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width),
1065 Dirty->top / static_cast<FLOAT>(ThisDesc->Height));
1072 (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
1073 -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
1077 (DestDirty.left - CenterX) / static_cast<FLOAT>(CenterX),
1078 -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
1082 (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
1083 -1 * (DestDirty.bottom - CenterY) / static_cast<FLOAT>(CenterY),
1085 Vertices[3].Pos = Vertices[2].Pos;
1086 Vertices[4].Pos = Vertices[1].Pos;
1089 (DestDirty.right - CenterX) / static_cast<FLOAT>(CenterX),
1090 -1 * (DestDirty.top - CenterY) / static_cast<FLOAT>(CenterY),
1093 Vertices[3].TexCoord = Vertices[2].TexCoord;
1094 Vertices[4].TexCoord = Vertices[1].TexCoord;
1098 MaybeReallocVertexBuffer (UINT buffer_size)
1100 if (buffer_size <= vertex_buffer_size_)
1104 delete[] vertex_buffer_;
1106 vertex_buffer_ = new BYTE[buffer_size];
1107 vertex_buffer_size_ = buffer_size;
1111 CopyDirty (ID3D11Texture2D* SrcSurface, ID3D11Texture2D* SharedSurf,
1112 RECT* DirtyBuffer, UINT DirtyCount, DXGI_OUTPUT_DESC* DeskDesc)
1114 D3D11_TEXTURE2D_DESC FullDesc;
1115 D3D11_TEXTURE2D_DESC ThisDesc;
1116 ComPtr<ID3D11ShaderResourceView> ShaderResource;
1117 ComPtr<ID3D11Buffer> VertBuf;
1119 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
1120 ID3D11DeviceContext *device_context =
1121 gst_d3d11_device_get_device_context_handle (device_);
1123 GST_TRACE ("Copying DiretyRects (count %d)", DirtyCount);
1125 SharedSurf->GetDesc(&FullDesc);
1126 SrcSurface->GetDesc(&ThisDesc);
1129 hr = device_handle->CreateRenderTargetView(SharedSurf, nullptr, &rtv_);
1130 if (!gst_d3d11_result (hr, device_)) {
1131 GST_ERROR ("Couldn't create renter target view, hr 0x%x", (guint) hr);
1132 return GST_FLOW_ERROR;
1136 D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
1137 ShaderDesc.Format = ThisDesc.Format;
1138 ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1139 ShaderDesc.Texture2D.MostDetailedMip = ThisDesc.MipLevels - 1;
1140 ShaderDesc.Texture2D.MipLevels = ThisDesc.MipLevels;
1142 /* Create new shader resource view */
1143 hr = device_handle->CreateShaderResourceView(SrcSurface,
1144 &ShaderDesc, &ShaderResource);
1145 if (!gst_d3d11_result (hr, device_)) {
1146 return gst_d3d11_desktop_dup_return_from_hr(device_handle, hr,
1147 SystemTransitionsExpectedErrors);
1150 ID3D11SamplerState *samplers = sampler_.Get();
1151 ID3D11ShaderResourceView *srv = ShaderResource.Get();
1152 ID3D11RenderTargetView *rtv = rtv_.Get();
1153 device_context->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
1154 device_context->OMSetRenderTargets(1, &rtv, nullptr);
1155 device_context->VSSetShader(vs_.Get(), nullptr, 0);
1156 device_context->PSSetShader(ps_.Get(), nullptr, 0);
1157 device_context->PSSetShaderResources(0, 1, &srv);
1158 device_context->PSSetSamplers(0, 1, &samplers);
1159 device_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1160 device_context->IASetInputLayout(layout_.Get());
1162 /* Create space for vertices for the dirty rects if the current space isn't
1164 UINT byte_needed = sizeof(VERTEX) * NUMVERTICES * DirtyCount;
1165 MaybeReallocVertexBuffer (byte_needed);
1168 VERTEX* DirtyVertex = (VERTEX *) vertex_buffer_;
1169 for (UINT i = 0; i < DirtyCount; ++i, DirtyVertex += NUMVERTICES) {
1170 SetDirtyVert(DirtyVertex, &DirtyBuffer[i], DeskDesc,
1171 &FullDesc, &ThisDesc);
1174 /* Create vertex buffer */
1175 D3D11_BUFFER_DESC BufferDesc;
1176 memset (&BufferDesc, 0, sizeof (D3D11_BUFFER_DESC));
1177 BufferDesc.Usage = D3D11_USAGE_DEFAULT;
1178 BufferDesc.ByteWidth = byte_needed;
1179 BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1180 BufferDesc.CPUAccessFlags = 0;
1181 D3D11_SUBRESOURCE_DATA InitData;
1182 memset (&InitData, 0, sizeof (D3D11_SUBRESOURCE_DATA));
1183 InitData.pSysMem = vertex_buffer_;
1185 hr = device_handle->CreateBuffer(&BufferDesc, &InitData, &VertBuf);
1186 if (!gst_d3d11_result (hr, device_)) {
1187 GST_ERROR ("Failed to create vertex buffer");
1188 return GST_FLOW_ERROR;
1191 UINT Stride = sizeof(VERTEX);
1193 ID3D11Buffer *vert_buf = VertBuf.Get();
1194 device_context->IASetVertexBuffers(0, 1, &vert_buf, &Stride, &Offset);
1197 VP.Width = static_cast<FLOAT>(FullDesc.Width);
1198 VP.Height = static_cast<FLOAT>(FullDesc.Height);
1203 device_context->RSSetViewports(1, &VP);
1205 device_context->Draw(NUMVERTICES * DirtyCount, 0);
1207 /* Unbind srv and rtv from context */
1209 device_context->PSSetShaderResources (0, 1, &srv);
1210 device_context->OMSetRenderTargets (0, nullptr, nullptr);
1216 ProcessFrame(ID3D11Texture2D * acquired_texture, ID3D11Texture2D* SharedSurf,
1217 DXGI_OUTPUT_DESC* DeskDesc, UINT move_count, UINT dirty_count,
1218 DXGI_OUTDUPL_FRAME_INFO * frame_info)
1220 GstFlowReturn ret = GST_FLOW_OK;
1222 GST_TRACE ("Processing frame");
1224 /* Process dirties and moves */
1225 if (frame_info->TotalMetadataBufferSize) {
1227 ret = CopyMove(SharedSurf, (DXGI_OUTDUPL_MOVE_RECT *) metadata_buffer_,
1228 move_count, DeskDesc);
1230 if (ret != GST_FLOW_OK)
1235 ret = CopyDirty(acquired_texture, SharedSurf,
1236 (RECT *)(metadata_buffer_ +
1237 (move_count * sizeof(DXGI_OUTDUPL_MOVE_RECT))),
1238 dirty_count, DeskDesc);
1241 GST_TRACE ("No metadata");
1249 ProcessMonoMask (bool IsMono, PTR_INFO* PtrInfo, INT* PtrWidth,
1250 INT* PtrHeight, INT* PtrLeft, INT* PtrTop, BYTE** InitBuffer,
1253 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device_);
1254 ID3D11DeviceContext *context_handle =
1255 gst_d3d11_device_get_device_context_handle (device_);
1257 D3D11_TEXTURE2D_DESC FullDesc;
1258 shared_texture_->GetDesc(&FullDesc);
1259 INT DesktopWidth = FullDesc.Width;
1260 INT DesktopHeight = FullDesc.Height;
1263 INT GivenLeft = PtrInfo->Position.x;
1264 INT GivenTop = PtrInfo->Position.y;
1266 // Figure out if any adjustment is needed for out of bound positions
1267 if (GivenLeft < 0) {
1268 *PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width);
1269 } else if ((GivenLeft + static_cast<INT>(PtrInfo->shape_info.Width)) > DesktopWidth) {
1270 *PtrWidth = DesktopWidth - GivenLeft;
1272 *PtrWidth = static_cast<INT>(PtrInfo->shape_info.Width);
1276 PtrInfo->shape_info.Height = PtrInfo->shape_info.Height / 2;
1279 *PtrHeight = GivenTop + static_cast<INT>(PtrInfo->shape_info.Height);
1280 } else if ((GivenTop + static_cast<INT>(PtrInfo->shape_info.Height)) > DesktopHeight) {
1281 *PtrHeight = DesktopHeight - GivenTop;
1283 *PtrHeight = static_cast<INT>(PtrInfo->shape_info.Height);
1287 PtrInfo->shape_info.Height = PtrInfo->shape_info.Height * 2;
1289 *PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft;
1290 *PtrTop = (GivenTop < 0) ? 0 : GivenTop;
1292 D3D11_TEXTURE2D_DESC CopyBufferDesc;
1293 CopyBufferDesc.Width = *PtrWidth;
1294 CopyBufferDesc.Height = *PtrHeight;
1295 CopyBufferDesc.MipLevels = 1;
1296 CopyBufferDesc.ArraySize = 1;
1297 CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1298 CopyBufferDesc.SampleDesc.Count = 1;
1299 CopyBufferDesc.SampleDesc.Quality = 0;
1300 CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
1301 CopyBufferDesc.BindFlags = 0;
1302 CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
1303 CopyBufferDesc.MiscFlags = 0;
1305 ComPtr<ID3D11Texture2D> CopyBuffer;
1306 HRESULT hr = device_handle->CreateTexture2D(&CopyBufferDesc,
1307 nullptr, &CopyBuffer);
1308 if (!gst_d3d11_result (hr, device_)) {
1309 GST_ERROR ("Couldn't create texture for mouse pointer");
1313 Box->left = *PtrLeft;
1315 Box->right = *PtrLeft + *PtrWidth;
1316 Box->bottom = *PtrTop + *PtrHeight;
1317 context_handle->CopySubresourceRegion(CopyBuffer.Get(),
1318 0, 0, 0, 0, shared_texture_.Get(), 0, Box);
1320 ComPtr<IDXGISurface> CopySurface;
1321 hr = CopyBuffer.As (&CopySurface);
1322 if (!gst_d3d11_result (hr, device_)) {
1323 GST_ERROR ("Couldn't get DXGI resource from mouse texture");
1327 DXGI_MAPPED_RECT MappedSurface;
1328 hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
1329 if (!gst_d3d11_result (hr, device_)) {
1330 GST_ERROR ("Couldn't map DXGI surface");
1334 *InitBuffer = new BYTE[*PtrWidth * *PtrHeight * BPP];
1336 UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer);
1337 UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits);
1338 UINT DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT);
1340 // What to skip (pixel offset)
1341 UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0);
1342 UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0);
1345 for (INT Row = 0; Row < *PtrHeight; Row++) {
1347 Mask = Mask >> (SkipX % 8);
1348 for (INT Col = 0; Col < *PtrWidth; Col++) {
1349 BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
1350 ((Row + SkipY) * (PtrInfo->shape_info.Pitch))] & Mask;
1351 BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) +
1352 ((Row + SkipY + (PtrInfo->shape_info.Height / 2)) *
1353 (PtrInfo->shape_info.Pitch))] & Mask;
1354 UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
1355 UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
1357 InitBuffer32[(Row * *PtrWidth) + Col] =
1358 (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32;
1368 UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer);
1370 for (INT Row = 0; Row < *PtrHeight; Row++) {
1371 for (INT Col = 0; Col < *PtrWidth; ++Col) {
1373 UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) *
1374 (PtrInfo->shape_info.Pitch / sizeof(UINT)))];
1377 InitBuffer32[(Row * *PtrWidth) + Col] =
1378 (Desktop32[(Row * DesktopPitchInPixels) + Col] ^
1379 Buffer32[(Col + SkipX) + ((Row + SkipY) *
1380 (PtrInfo->shape_info.Pitch / sizeof(UINT)))]) | 0xFF000000;
1383 InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) +
1384 ((Row + SkipY) * (PtrInfo->shape_info.Pitch / sizeof(UINT)))] | 0xFF000000;
1390 // Done with resource
1391 hr = CopySurface->Unmap();
1392 if (!gst_d3d11_result (hr, device_)) {
1393 GST_ERROR ("Failed to unmap DXGI surface");
1402 DXGI_OUTPUT_DESC output_desc_;
1403 GstD3D11Device * device_;
1405 ComPtr<ID3D11Texture2D> shared_texture_;
1406 ComPtr<ID3D11RenderTargetView> rtv_;
1407 ComPtr<ID3D11Texture2D> move_texture_;
1408 ComPtr<ID3D11VertexShader> vs_;
1409 ComPtr<ID3D11PixelShader> ps_;
1410 ComPtr<ID3D11InputLayout> layout_;
1411 ComPtr<ID3D11SamplerState> sampler_;
1412 ComPtr<IDXGIOutputDuplication> dupl_;
1413 ComPtr<ID3D11BlendState> blend_;
1415 /* frame metadata */
1416 BYTE *metadata_buffer_;
1417 UINT metadata_buffer_size_;
1419 /* vertex buffers */
1420 BYTE *vertex_buffer_;
1421 UINT vertex_buffer_size_;
1425 struct _GstD3D11DesktopDup
1429 GstD3D11Device *device;
1433 ID3D11Texture2D *texture;
1434 D3D11DesktopDupObject *dupl_obj;
1438 RECT desktop_coordinates;
1444 static void gst_d3d11_desktop_dup_constructed (GObject * object);
1445 static void gst_d3d11_desktop_dup_dispose (GObject * object);
1446 static void gst_d3d11_desktop_dup_finalize (GObject * object);
1447 static void gst_d3d11_desktop_dup_set_property (GObject * object, guint prop_id,
1448 const GValue * value, GParamSpec * pspec);
1450 #define gst_d3d11_desktop_dup_parent_class parent_class
1451 G_DEFINE_TYPE (GstD3D11DesktopDup, gst_d3d11_desktop_dup, GST_TYPE_OBJECT);
1454 gst_d3d11_desktop_dup_class_init (GstD3D11DesktopDupClass * klass)
1456 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1458 gobject_class->constructed = gst_d3d11_desktop_dup_constructed;
1459 gobject_class->dispose = gst_d3d11_desktop_dup_dispose;
1460 gobject_class->finalize = gst_d3d11_desktop_dup_finalize;
1461 gobject_class->set_property = gst_d3d11_desktop_dup_set_property;
1463 g_object_class_install_property (gobject_class, PROP_D3D11_DEVICE,
1464 g_param_spec_object ("d3d11device", "D3D11 Device",
1465 "GstD3D11Device object for operating",
1466 GST_TYPE_D3D11_DEVICE, (GParamFlags)
1467 (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
1468 G_PARAM_STATIC_STRINGS)));
1469 g_object_class_install_property (gobject_class, PROP_OUTPUT_INDEX,
1470 g_param_spec_int ("monitor-index", "Monitor Index",
1471 "Zero-based index for monitor to capture (-1 = primary monitor)",
1472 -1, G_MAXINT, DEFAULT_MONITOR_INDEX, (GParamFlags)
1473 (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
1474 G_PARAM_STATIC_STRINGS)));
1478 gst_d3d11_desktop_dup_init (GstD3D11DesktopDup * self)
1480 self->monitor_index = DEFAULT_MONITOR_INDEX;
1481 g_rec_mutex_init (&self->lock);
1483 memset (&self->desktop_coordinates, 0, sizeof (RECT));
1487 gst_d3d11_desktop_dup_get_monitor_size (GstD3D11DesktopDup * self,
1488 HMONITOR hmonitor, RECT * size)
1490 MONITORINFOEX monitor_info;
1493 monitor_info.cbSize = sizeof (MONITORINFOEX);
1494 if (!GetMonitorInfo (hmonitor, (LPMONITORINFO) & monitor_info)) {
1495 GST_WARNING_OBJECT (self, "Couldn't get monitor info");
1499 dev_mode.dmSize = sizeof (DEVMODE);
1500 dev_mode.dmDriverExtra = sizeof (POINTL);
1501 dev_mode.dmFields = DM_POSITION;
1502 if (!EnumDisplaySettings
1503 (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
1504 GST_WARNING_OBJECT (self, "Couldn't enumerate display settings");
1508 size->left = dev_mode.dmPosition.x;
1509 size->top = dev_mode.dmPosition.y;
1510 size->right = size->left + dev_mode.dmPelsWidth;
1511 size->bottom = size->top + dev_mode.dmPelsHeight;
1517 gst_d3d11_desktop_dup_constructed (GObject * object)
1519 GstD3D11DesktopDup *self = GST_D3D11_DESKTOP_DUP (object);
1521 ComPtr<IDXGIDevice> dxgi_device;
1522 ComPtr<IDXGIAdapter> adapter;
1523 ComPtr<IDXGIOutput> output;
1524 ComPtr<IDXGIOutput1> output1;
1526 ID3D11Device *device_handle;
1528 gboolean ret = FALSE;
1529 DXGI_OUTPUT_DESC output_desc;
1531 if (!self->device) {
1532 GST_WARNING_OBJECT (self, "D3D11 device is unavailable");
1536 device_handle = gst_d3d11_device_get_device_handle (self->device);
1538 /* Below code is just for getting resolution of IDXGIOutput (i.e., monitor)
1539 * and we will setup IDXGIOutputDuplication interface later.
1541 hr = device_handle->QueryInterface (IID_PPV_ARGS (&dxgi_device));
1542 if (!gst_d3d11_result (hr, self->device))
1545 hr = dxgi_device->GetParent (IID_PPV_ARGS (&adapter));
1546 if (!gst_d3d11_result (hr, self->device))
1549 if (self->monitor_index < 0) {
1551 /* Enumerate all outputs to find primary monitor */
1553 hr = adapter->EnumOutputs (index, output.ReleaseAndGetAddressOf ());
1554 if (!gst_d3d11_result (hr, self->device))
1557 output->GetDesc (&output_desc);
1558 if (output_desc.DesktopCoordinates.left == 0 &&
1559 output_desc.DesktopCoordinates.top == 0) {
1560 GST_DEBUG_OBJECT (self, "Found primary output, index %d", index);
1561 self->monitor_index = index;
1562 self->primary = TRUE;
1566 } while (gst_d3d11_result (hr, self->device));
1568 hr = adapter->EnumOutputs (self->monitor_index, &output);
1569 if (!gst_d3d11_result (hr, self->device)) {
1570 GST_WARNING_OBJECT (self, "No available output");
1574 output->GetDesc (&output_desc);
1575 if (output_desc.DesktopCoordinates.left == 0 &&
1576 output_desc.DesktopCoordinates.top == 0) {
1577 GST_DEBUG_OBJECT (self, "We are primary output");
1578 self->primary = TRUE;
1582 hr = output.As (&output1);
1583 if (!gst_d3d11_result (hr, self->device)) {
1584 GST_WARNING_OBJECT (self, "IDXGIOutput1 interface is unavailble");
1588 /* DesktopCoordinates will not report actual texture size in case that
1589 * application is running without dpi-awareness. To get actual monitor size,
1590 * we need to use Win32 API... */
1591 if (!gst_d3d11_desktop_dup_get_monitor_size (self,
1592 output_desc.Monitor, &self->desktop_coordinates)) {
1597 self->desktop_coordinates.right - self->desktop_coordinates.left;
1599 self->desktop_coordinates.bottom - self->desktop_coordinates.top;
1601 GST_DEBUG_OBJECT (self,
1602 "Desktop coordinates left:top:right:bottom = %ld:%ld:%ld:%ld (%dx%d)",
1603 self->desktop_coordinates.left, self->desktop_coordinates.top,
1604 self->desktop_coordinates.right, self->desktop_coordinates.bottom,
1605 self->width, self->height);
1611 gst_clear_object (&self->device);
1615 gst_d3d11_desktop_dup_set_property (GObject * object, guint prop_id,
1616 const GValue * value, GParamSpec * pspec)
1618 GstD3D11DesktopDup *self = GST_D3D11_DESKTOP_DUP (object);
1621 case PROP_D3D11_DEVICE:
1622 self->device = (GstD3D11Device *) g_value_dup_object (value);
1624 case PROP_OUTPUT_INDEX:
1625 self->monitor_index = g_value_get_int (value);
1628 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1634 gst_d3d11_desktop_dup_dispose (GObject * object)
1636 GstD3D11DesktopDup *self = GST_D3D11_DESKTOP_DUP (object);
1638 GST_D3D11_CLEAR_COM (self->texture);
1640 if (self->dupl_obj) {
1641 delete self->dupl_obj;
1642 self->dupl_obj = nullptr;
1645 gst_clear_object (&self->device);
1647 G_OBJECT_CLASS (parent_class)->dispose (object);
1651 gst_d3d11_desktop_dup_finalize (GObject * object)
1653 GstD3D11DesktopDup *self = GST_D3D11_DESKTOP_DUP (object);
1655 g_rec_mutex_clear (&self->lock);
1657 G_OBJECT_CLASS (parent_class)->finalize (object);
1661 gst_d3d11_desktop_dup_setup_texture (GstD3D11DesktopDup * self)
1663 D3D11_TEXTURE2D_DESC texture_desc = { 0, };
1664 ID3D11Device *device_handle;
1667 ComPtr<ID3D11Texture2D> texture;
1670 /* This texture is for copying/updating only updated region from previously
1671 * captured frame (like a reference frame) */
1672 device_handle = gst_d3d11_device_get_device_handle (self->device);
1674 texture_desc.Width = self->width;
1675 texture_desc.Height = self->height;
1676 texture_desc.MipLevels = 1;
1677 texture_desc.ArraySize = 1;
1678 /* FIXME: we can support DXGI_FORMAT_R10G10B10A2_UNORM */
1679 texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1680 texture_desc.SampleDesc.Count = 1;
1681 texture_desc.Usage = D3D11_USAGE_DEFAULT;
1682 texture_desc.BindFlags =
1683 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
1684 texture_desc.CPUAccessFlags = 0;
1685 texture_desc.MiscFlags = 0;
1687 hr = device_handle->CreateTexture2D (&texture_desc, nullptr, &texture);
1688 if (!gst_d3d11_result (hr, self->device)) {
1689 GST_ERROR_OBJECT (self, "Couldn't create texture, hr 0x%x", (guint) hr);
1693 self->texture = texture.Detach ();
1699 gst_d3d11_desktop_dup_weak_ref_notify (gpointer data, GstD3D11DesktopDup * dupl)
1701 G_LOCK (dupl_list_lock);
1702 dupl_list = g_list_remove (dupl_list, dupl);
1703 G_UNLOCK (dupl_list_lock);
1706 GstD3D11DesktopDup *
1707 gst_d3d11_desktop_dup_new (GstD3D11Device * device, gint monitor_index)
1709 GstD3D11DesktopDup *self = nullptr;
1712 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), nullptr);
1714 /* Check if we have dup object corresponding to monitor_index, and if there is
1715 * already configured capture object, reuse it.
1716 * This is because of the limitation of desktop duplication API
1717 * (i.e., in a process, only one duplication object can exist).
1719 * https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutput1-duplicateoutput#remarks
1721 G_LOCK (dupl_list_lock);
1722 for (iter = dupl_list; iter; iter = g_list_next (iter)) {
1723 GstD3D11DesktopDup *dupl = (GstD3D11DesktopDup *) iter->data;
1725 if (dupl->monitor_index == monitor_index ||
1726 (monitor_index < 0 && dupl->primary)) {
1727 GST_DEBUG ("Found configured desktop dup object for output index %d",
1729 self = (GstD3D11DesktopDup *) gst_object_ref (dupl);
1735 G_UNLOCK (dupl_list_lock);
1739 self = (GstD3D11DesktopDup *) g_object_new (GST_TYPE_D3D11_DESKTOP_DUP,
1740 "d3d11device", device, "monitor-index", monitor_index, nullptr);
1742 if (!self->device) {
1743 GST_WARNING_OBJECT (self, "Couldn't configure desktop dup object");
1744 gst_object_unref (self);
1745 G_UNLOCK (dupl_list_lock);
1750 g_object_weak_ref (G_OBJECT (self),
1751 (GWeakNotify) gst_d3d11_desktop_dup_weak_ref_notify, nullptr);
1752 dupl_list = g_list_append (dupl_list, self);
1754 G_UNLOCK (dupl_list_lock);
1760 gst_d3d11_desktop_dup_prepare (GstD3D11DesktopDup * desktop)
1764 g_return_val_if_fail (GST_IS_D3D11_DESKTOP_DUP (desktop), GST_FLOW_ERROR);
1765 g_return_val_if_fail (desktop->device != nullptr, GST_FLOW_ERROR);
1767 g_rec_mutex_lock (&desktop->lock);
1768 if (desktop->prepared) {
1769 GST_DEBUG_OBJECT (desktop, "Already prepared");
1770 g_rec_mutex_unlock (&desktop->lock);
1774 if (!desktop->texture && !gst_d3d11_desktop_dup_setup_texture (desktop)) {
1775 GST_ERROR_OBJECT (desktop, "Couldn't setup internal texture");
1776 g_rec_mutex_unlock (&desktop->lock);
1777 return GST_FLOW_ERROR;
1780 desktop->dupl_obj = new D3D11DesktopDupObject ();
1781 ret = desktop->dupl_obj->Init (desktop->device, desktop->texture,
1782 desktop->monitor_index);
1783 if (ret != GST_FLOW_OK) {
1784 GST_WARNING_OBJECT (desktop,
1785 "Couldn't prepare capturing, %sexpected failure",
1786 ret == GST_D3D11_DESKTOP_DUP_FLOW_EXPECTED_ERROR ? "" : "un");
1788 delete desktop->dupl_obj;
1789 desktop->dupl_obj = nullptr;
1790 g_rec_mutex_unlock (&desktop->lock);
1795 desktop->prepared = TRUE;
1796 g_rec_mutex_unlock (&desktop->lock);
1802 gst_d3d11_desktop_dup_get_coordinates (GstD3D11DesktopDup * desktop,
1803 RECT * desktop_coordinates)
1805 g_return_val_if_fail (GST_IS_D3D11_DESKTOP_DUP (desktop), FALSE);
1806 g_return_val_if_fail (desktop_coordinates != nullptr, FALSE);
1808 *desktop_coordinates = desktop->desktop_coordinates;
1814 gst_d3d11_desktop_dup_capture (GstD3D11DesktopDup * desktop,
1815 ID3D11Texture2D * texture, ID3D11RenderTargetView * rtv,
1816 gboolean draw_mouse)
1818 GstFlowReturn ret = GST_FLOW_OK;
1819 ID3D11DeviceContext *device_context_handle;
1821 g_return_val_if_fail (GST_IS_D3D11_DESKTOP_DUP (desktop), GST_FLOW_ERROR);
1822 g_return_val_if_fail (texture != nullptr, GST_FLOW_ERROR);
1824 g_rec_mutex_lock (&desktop->lock);
1825 if (!desktop->prepared)
1826 ret = gst_d3d11_desktop_dup_prepare (desktop);
1828 if (ret != GST_FLOW_OK) {
1829 GST_WARNING_OBJECT (desktop, "We are not prepared");
1830 g_rec_mutex_unlock (&desktop->lock);
1834 gst_d3d11_device_lock (desktop->device);
1835 ret = desktop->dupl_obj->Capture (draw_mouse);
1836 if (ret != GST_FLOW_OK) {
1837 gst_d3d11_device_unlock (desktop->device);
1839 delete desktop->dupl_obj;
1840 desktop->dupl_obj = nullptr;
1841 desktop->prepared = FALSE;
1843 if (ret == GST_D3D11_DESKTOP_DUP_FLOW_EXPECTED_ERROR) {
1844 GST_WARNING_OBJECT (desktop,
1845 "Couldn't capture frame, but expected failure");
1847 GST_ERROR_OBJECT (desktop, "Unexpected failure during capture");
1850 g_rec_mutex_unlock (&desktop->lock);
1854 GST_LOG_OBJECT (desktop, "Capture done");
1856 device_context_handle =
1857 gst_d3d11_device_get_device_context_handle (desktop->device);
1858 device_context_handle->CopySubresourceRegion (texture, 0, 0, 0, 0,
1859 desktop->texture, 0, nullptr);
1861 desktop->dupl_obj->DrawMouse (rtv);
1862 gst_d3d11_device_unlock (desktop->device);
1863 g_rec_mutex_unlock (&desktop->lock);