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:
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.
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.
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;
}