Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ScreenCaptureforHWND: How to get the byte data of the captured frame? #132

Closed
DobyChao opened this issue Apr 18, 2024 · 4 comments
Closed

Comments

@DobyChao
Copy link

I've built a DLL project to capture window screens, based on the ScreenCaptureforHWND example. However, all the frames I captured are zeros. How can I get the byte data?
Here's my capture code (repository here):

bool DXGICapture::Grab(BYTE* buffer)
{
	com_ptr<ID3D11Texture2D> backBuffer;
	check_hresult(m_swapChain->GetBuffer(0, guid_of<ID3D11Texture2D>(), backBuffer.put_void()));

	// create staging texture
	D3D11_TEXTURE2D_DESC desc;
	backBuffer->GetDesc(&desc);
	auto width = desc.Width;
	auto height = desc.Height;

	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	desc.Usage = D3D11_USAGE_STAGING;
	desc.BindFlags = 0;
	desc.MiscFlags = 0;

	com_ptr<ID3D11Texture2D> stagingTexture { nullptr };
	check_hresult(m_d3dDevice->CreateTexture2D(&desc, nullptr, stagingTexture.put()));

	m_d3dContext->CopyResource(stagingTexture.get(), backBuffer.get());

	D3D11_MAPPED_SUBRESOURCE resource;
	check_hresult(m_d3dContext->Map(stagingTexture.get(), NULL, D3D11_MAP_READ, 0, &resource));

	const size_t bufferSize = width * height * 4;
	memcpy(buffer, resource.pData, bufferSize);

	m_d3dContext->Unmap(stagingTexture.get(), 0);

	return true;
}
@robmikh
Copy link
Member

robmikh commented Apr 18, 2024

First, make sure you're setting the CPUAccessFlags to D3D11_CPU_ACCESS_READ. Second, I wouldn't bother with the swap chain unless you intend to put the swap chain on screen. The sample uses it purely to show you what the capture looks like. You probably want to use the capture API like this: https://github.com/robmikh/CaptureVideoSample/blob/master/CaptureVideoSample/CaptureFrameGenerator.cpp

When frames come in they will be queued up, and calling TryGetNextFrame will give you a queued frame or block until it receives one.

@robmikh
Copy link
Member

robmikh commented Apr 18, 2024

Oh, one more thing, you'll want to observe the RowPitch field from the D3D11_MAPPED_SUBRESOURCE struct when accessing the bytes of the texture. The stride of your texture may exceed the width. See this helper method:

https://github.com/robmikh/robmikh.common/blob/010a4149e86db95dd9f6957ca99f4311ab8bcbcb/robmikh.common/include/robmikh.common/d3d11Helpers.h#L206-L237

@DobyChao
Copy link
Author

DobyChao commented Apr 21, 2024

Thanks so much for your reply! I've updated my code here based on your suggestions, but still can't capture the image. The OnFrameArrived function doesn't seem to be triggered. What should I do next?
Here‘s my test code:

// add DXGICapture.lib to library

#include "pch.h"
#include <iostream>
#include "DXGICapture.h"

int main()
{
	HWND hwnd = FindWindow(NULL, L"Ice.mp4 - PotPlayer");
	if (hwnd == NULL)
	{
		std::cout << "FindWindow failed" << std::endl;
		return 1;
	}
	auto cap = CreateCapture(hwnd);
	StartCapture(cap);
	BYTE* buffer = new BYTE[1920 * 1080 * 4];
	Grab(cap, buffer);
	// delay
	Sleep(1000);
}

@robmikh
Copy link
Member

robmikh commented Apr 22, 2024

Since you're calling Direct3D11CaptureFramePool::Create, you need to be pumping messages. Given that you're using a lock in the FrameArrived callback, you should call Direct3D11CaptureFramePool::CreateFreeThreaded instead. That removes the message pumping requirement and causes the FrameArrived callback to fire on an internal worker thread.

@DobyChao DobyChao closed this as completed Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants