AcquireNextFrame 不工作(桌面复制 API 和 D3D11)
Posted
技术标签:
【中文标题】AcquireNextFrame 不工作(桌面复制 API 和 D3D11)【英文标题】:AcquireNextFrame not working (Desktop Duplication API & D3D11) 【发布时间】:2018-09-04 00:11:30 【问题描述】:我已经整理了这段代码,它截取了桌面的屏幕截图并将其映射为原始像素数据访问,但输出全为零。我不知道我做错了什么。在网上查看了许多 Desktop Duplication Api 示例后,我发现它们与我的没有任何区别。
这是我初始化所有内容的方法,不会引发任何错误。
BOOL init()
CHECKHR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
IDXGIDevice* lDxgiDevice;
CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
IDXGIAdapter* lDxgiAdapter;
CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
lDxgiDevice->Release();
IDXGIOutput* lDxgiOutput;
CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
lDxgiAdapter->Release();
CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
IDXGIOutput1* lDxgiOutput1;
CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
lDxgiOutput->Release();
CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
lDxgiOutput1->Release();
lDeskDupl->GetDesc(&OutputDuplDesc);
D3D11_TEXTURE2D_DESC desc;
desc.Width = OutputDuplDesc.ModeDesc.Width;
desc.Height = OutputDuplDesc.ModeDesc.Height;
desc.Format = OutputDuplDesc.ModeDesc.Format;
desc.ArraySize = 1;
desc.BindFlags = 0;
desc.MiscFlags = 0;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MipLevels = 1;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
desc.Usage = D3D11_USAGE_STAGING;
CHECKHR(lDevice->CreateTexture2D(&desc, NULL, &lDestImage))
return TRUE;
我正在使用的 CHECKHR 宏已经过我的测试并且可以正常工作,只是为了澄清这不是问题。
这是我用来实际抓取框架的代码:
int main()
init();
HRESULT hr;
IDXGIResource* lDesktopResource;
ID3D11Texture2D* lAcquiredDesktopImage;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
while (true)
if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)))
break;
hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage));
if (FAILED(hr))
cout << "QueryInterface failed!" << endl;
system("pause");
lDesktopResource->Release();
lImmediateContext->CopyResource(lDestImage, lAcquiredDesktopImage);
lAcquiredDesktopImage->Release();
lDeskDupl->ReleaseFrame();
D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
hr = lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
if (FAILED(hr))
cout << "Map failed!" << endl;
system("pause");
BYTE* pb = (BYTE*)(resource.pData);
for (int i = 0; i < 2000000; i++)
cout << (int)pb[i] << endl;
system("pause");
return 0;
所发生的只是 2000000 个零被打印到控制台。有什么我遗漏或看不到的东西吗?
【问题讨论】:
我想D3D11CreateDevice
调用是错误的。您应该先使用 DXGI 获取适配器指针,然后以显式适配器作为第一个参数调用 D3D11CreateDevice
。反之亦然。
听从 Roman R 的建议,我创建了另一个使用 IDXGIFactory 获取适配器的 init 方法,然后使用它创建设备。但现在对 D3D11CreateDevice 的调用返回 E_INVALIDARG。在 IDXGIFactory::EnumAdapters 中使用 0 作为索引参数是否正确?
这里是我的新 init 方法的链接。 https://pastebin.com/fAPyqWMa
新的错误很可能是D3D_DRIVER_TYPE_HARDWARE
引起的,需要改成D3D_DRIVER_TYPE_UNKNOWN
。 Enable D3D debug layer 在调试输出窗口中启用提示。
【参考方案1】:
首先,针对您要复制的输出的特定适配器调用D3D11CreateDevice
:
BOOL init()
IDXGIFactory* lDxgiFactory;
CHECKHR(CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&lDxgiFactory))
IDXGIAdapter* lDxgiAdapter;
CHECKHR(lDxgiFactory->EnumAdapters(0, &lDxgiAdapter))
lDxgiFactory->Release();
CHECKHR(D3D11CreateDevice(lDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
IDXGIDevice* lDxgiDevice;
CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
//IDXGIAdapter* lDxgiAdapter;
//CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
lDxgiDevice->Release();
IDXGIOutput* lDxgiOutput;
CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
lDxgiAdapter->Release();
CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
IDXGIOutput1* lDxgiOutput1;
CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
lDxgiOutput->Release();
CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
// ...
那么,您的代码在AcquireNextFrame
附近不太准确。您需要等待实际帧并在您跳过时在循环中执行ReleaseFrame
。
// ...
while (true)
if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)) && FrameInfo.LastPresentTime.QuadPart)
break;
lDeskDupl->ReleaseFrame();
// ...
【讨论】:
非常感谢!这修复了 init 函数,但直到我在 if 语句中添加“FrameInfo.LastPresentTime.QuadPart”之后它才真正起作用。我真的不知道它是什么,但它现在有效!以上是关于AcquireNextFrame 不工作(桌面复制 API 和 D3D11)的主要内容,如果未能解决你的问题,请参考以下文章