0

I asked in a previous question whether there was a way of capturing a window in Win 10 without using WinRT (Capturing a window on Win 10 without WinRT). It would seem, after a lot of research, there isn't right now. I found this answer posted by gil123: Screen Capture Specific Window. My problem is three fold:

  1. I don't want to just capture a screen. I want to share a screen (send many consecutive frames). Is there a way for me to organize the code into an initialization function, capturing function that returns an image and a shutdown/cleanup function? Right now, the code above creates a monolithe - I need to initialize every time I capture.

  2. I don't have access to the IsBorderRequired property found here: https://github.com/robmikh/Win32CaptureSample/blob/d5c1afb164147c983c078bf9b9e596cc9589db0c/Win32CaptureSample/SampleWindow.cpp#L224 https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturesession.isborderrequired?view=winrt-22621 possibly because I'm on Win 10. Can somebody suggest another way I can remove the border? Otherwise, a border appears around the window I am screen capturing and flashes because I'm taking consecutive frames.

  3. I am getting a distorted image when the window isn't fullscreen it would seem. If somebody can spot the problem, let me know. I suspected that the problem was that the buffer size is incorrect but, as you can see from the code snippet, it is a function of the window size.

MyImageContainer MyClassFunction::captureFrame ( WindowInfo& window )
{
HWND hwnd = reinterpret_cast<HWND>(window.handle);

// Init COM
winrt::init_apartment ( winrt::apartment_type::multi_threaded );

// Create Direct 3D Device
winrt::com_ptr<ID3D11Device> d3dDevice;

winrt::check_hresult ( D3D11CreateDevice ( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, D3D11_SDK_VERSION, d3dDevice.put ( ), nullptr, nullptr ) );


winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device;
const auto dxgiDevice = d3dDevice.as<IDXGIDevice> ( );
{
winrt::com_ptr<::IInspectable> inspectable;
winrt::check_hresult ( CreateDirect3D11DeviceFromDXGIDevice ( dxgiDevice.get ( ), inspectable.put ( ) ) );
device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice> ( );
}


auto idxgiDevice2 = dxgiDevice.as<IDXGIDevice2> ( );
winrt::com_ptr<IDXGIAdapter> adapter;
winrt::check_hresult ( idxgiDevice2->GetParent ( winrt::guid_of<IDXGIAdapter> ( ), adapter.put_void ( ) ) );
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult ( adapter->GetParent ( winrt::guid_of<IDXGIFactory2> ( ), factory.put_void ( ) ) );

ID3D11DeviceContext* d3dContext = nullptr;
d3dDevice->GetImmediateContext ( &d3dContext );

RECT rect{};
DwmGetWindowAttribute ( hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof ( RECT ) );
const auto size = winrt::Windows::Graphics::SizeInt32{ rect.right - rect.left, rect.bottom - rect.top };

// Create frame pool
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool framePool = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create (
device,
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
2,
size
);

const auto activationFactory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem> ( );
auto interopFactory = activationFactory.as<IGraphicsCaptureItemInterop> ( );
winrt::Windows::Graphics::Capture::GraphicsCaptureItem captureItem = { nullptr };
interopFactory->CreateForWindow ( hwnd, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> ( ),
reinterpret_cast<void**>(winrt::put_abi ( captureItem )) );

// Assign the created frame pool and capture session to member variables
auto isFrameArrived = false;
winrt::com_ptr<ID3D11Texture2D> texture;

// Create capture session
winrt::Windows::Graphics::Capture::GraphicsCaptureSession session = framePool.CreateCaptureSession ( captureItem );
framePool.FrameArrived ( [&]( auto& pool, auto& )
{
if ( isFrameArrived ) return;
auto frame = pool.TryGetNextFrame ( );

struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown
{
virtual HRESULT __stdcall GetInterface ( GUID const& id, void** object ) = 0;
};

auto access = frame.Surface ( ).as<IDirect3DDxgiInterfaceAccess> ( );
access->GetInterface ( winrt::guid_of<ID3D11Texture2D> ( ), texture.put_void ( ) );
isFrameArrived = true;
return;
} );

//session.IsBorderRequired ( false );
// Start the capture session
session.StartCapture ( );

// Message pump
MSG msg;
clock_t timer = clock_t ( );
while ( !isFrameArrived )
{
if ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) > 0 )
DispatchMessage ( &msg );

if ( clock_t ( ) - timer > 20000 )
{
// TODO: try to make here a better error handling
return error "taking too long";
}
}

session.Close ( );

D3D11_TEXTURE2D_DESC ThisDesc = { 0 };
texture->GetDesc ( &ThisDesc );
D3D11_TEXTURE2D_DESC capturedTextureDesc;
capturedTextureDesc = ThisDesc;
capturedTextureDesc.Usage = D3D11_USAGE_STAGING;
capturedTextureDesc.BindFlags = 0;
capturedTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
capturedTextureDesc.MiscFlags = 0;
capturedTextureDesc.Height = window.size.y;
capturedTextureDesc.Width = window.size.x;

winrt::com_ptr<ID3D11Texture2D> userTexture = nullptr;
winrt::check_hresult ( d3dDevice->CreateTexture2D ( &capturedTextureDesc, NULL, userTexture.put ( ) ) );

d3dContext->CopyResource ( userTexture.get ( ), texture.get ( ) );


D3D11_MAPPED_SUBRESOURCE resource;
winrt::check_hresult ( d3dContext->Map ( userTexture.get ( ), 0, D3D11_MAP_READ, 0, &resource ) );

// Get the data
if ( !resource.pData )
{
return error "Can't get data";
}

// Unmap the user texture
d3dContext->Unmap ( userTexture.get ( ), 0 );

// Create an image object from the captured data
// create an empty imageBuffer that is size capturedTextureDesc.Width * capturedTextureDesc.Height * 4
UINT lBmpRowPitch = capturedTextureDesc.Width * 4;
auto sptr = static_cast<BYTE*>(resource.pData);
capturedTextureDesc.Height * 4 - lBmpRowPitch;
auto dptr = imageBuffer.data ( );

UINT lRowPitch = std::min<UINT> ( lBmpRowPitch, resource.RowPitch );

for ( size_t h = 0; h < capturedTextureDesc.Height; ++h )
{
memcpy_s ( dptr, lBmpRowPitch, sptr, lRowPitch );
sptr += resource.RowPitch;
dptr += lBmpRowPitch;
}

// data is imageBuffer with size capturedTextureDesc.Height * capturedTextureDesc.Width * 4

return ptrImg;
}
  • [GraphicsCaptureSession.IsBorderRequired](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturesession.isborderrequired?view=winrt-20348#applies-to) Property is introduced in Windows 10 Build 20348. [Windows.Graphics.Capture](https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture?view=winrt-20348) is recommended. For the 3rd, see the [ScreenCapture](https://github.com/WindowsNT/ScreenCapture/blob/713b4e1d85c2b4283aecf1f9d7957638c20b3bdb/capture.hpp#L702) sample. – YangXiaoPo-MSFT Jul 11 '23 at 02:00
  • Note that if you want to capture a whole screen (not target one window in particular), you don't need WinRT, you can use DXGI Output Duplication (the ancestor to Windows.Graphics.Capture), otherwise please provide a complete reproducible sample, our eyes can't compile not test partial code. – Simon Mourier Jul 11 '23 at 06:23
  • @YangXiaoPo-MSFT thank you for your reply. i only have Windows SDK Version 10.0.19041.0. Also the code you pointed to in ScreenCapture doesn't use WinRT. I am using WinRT now specifically to capture windows. I am successfully capturing and sharing desktops with the Duplication API. – bluetooth16 Jul 11 '23 at 16:00
  • @SimonMourier thank you for your reply. this code base uses a platform agnostic framework i have been building with a friend. i can't release it entirely but the winrt part is largely based on gil123's response. i am writing an interface and it works with the Duplication API and GDI+. my problem is that i can't capture a 4k monitor all the time - i want a window and i want to use the gpu. – bluetooth16 Jul 11 '23 at 16:07
  • 1
    gil123's code works fine. Please provide a complete reproducible sample https://stackoverflow.com/help/minimal-reproducible-example – Simon Mourier Jul 13 '23 at 14:32
  • are you able to capture windows with different sizes (not just full screen) using gil123's code? – bluetooth16 Jul 25 '23 at 16:59

0 Answers0