DirectX11+DXGI全屏屏幕捕获失效问题求助
解决DXGI+DirectX11捕获全屏内容失败的问题
嘿,我之前做类似的LED联动项目时,也踩过DXGI桌面复制的这个坑——正常窗口没问题,但全屏UWP应用(比如Netflix)或者独占全屏游戏就是抓不到内容,给你梳理下问题根源和可落地的解决办法:
问题根源
- UWP全屏应用的特殊渲染机制:Win10的UWP应用(比如Netflix)用的是CoreWindow,和传统Win32窗口的渲染管道不同,默认的DXGI桌面复制可能无法捕获其帧缓冲区
- 全屏独占游戏的显卡接管:很多全屏游戏会启用“独占模式”,直接接管显卡输出,这时候DXGI的
IDXGIOutputDuplication会失去访问权限,返回DXGI_ERROR_ACCESS_LOST错误 - DRM保护限制:像Netflix这类带DRM的视频应用,全屏播放时会加密帧数据,即使捕获成功也可能得到黑色画面,这是版权保护机制,无法绕过
针对性解决方案
1. 优化初始化函数,适配多显卡与UWP输出
你的初始化函数需要确保选中正确的显卡(尤其是核显+独显的机器,全屏应用可能切换到独显),并且正确枚举桌面输出设备:
HRESULT InitScreenCapture(ID3D11Device* pD3DDevice, IDXGIOutputDuplication** ppOutputDup) { HRESULT hr = S_OK; IDXGIFactory1* pFactory = nullptr; hr = CreateDXGIFactory1(IID_PPV_ARGS(&pFactory)); if (FAILED(hr)) return hr; IDXGIAdapter1* pAdapter = nullptr; // 遍历所有适配器,找到当前活跃的桌面输出适配器 for (UINT i = 0; pFactory->EnumAdapters1(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) { DXGI_ADAPTER_DESC1 adapterDesc; pAdapter->GetDesc1(&adapterDesc); IDXGIOutput* pOutput = nullptr; if (SUCCEEDED(pAdapter->EnumOutputs(0, &pOutput))) { DXGI_OUTPUT_DESC outputDesc; pOutput->GetDesc(&outputDesc); // 确认该输出是连接到桌面的主显示器 if (outputDesc.AttachedToDesktop && outputDesc.DesktopCoordinates.left == 0) { IDXGIOutput1* pOutput1 = nullptr; hr = pOutput->QueryInterface(IID_PPV_ARGS(&pOutput1)); if (SUCCEEDED(hr)) { // 创建输出复制对象,传入你的D3D11设备 hr = pOutput1->DuplicateOutput(pD3DDevice, ppOutputDup); pOutput1->Release(); } pOutput->Release(); if (SUCCEEDED(hr)) break; } } pAdapter->Release(); } pFactory->Release(); return hr; }
2. 捕获函数中处理权限丢失的情况
当全屏独占应用启动时,AcquireNextFrame会返回DXGI_ERROR_ACCESS_LOST,这时候需要重新初始化复制对象:
HRESULT CaptureFrame(IDXGIOutputDuplication* pOutputDup, ID3D11DeviceContext* pD3DContext, ID3D11Texture2D** ppCapturedTex) { HRESULT hr = S_OK; DXGI_OUTDUPL_FRAME_INFO frameInfo; IDXGIResource* pResource = nullptr; hr = pOutputDup->AcquireNextFrame(500, &frameInfo, &pResource); // 处理权限丢失,重新初始化捕获 if (hr == DXGI_ERROR_ACCESS_LOST) { pOutputDup->Release(); // 这里需要重新调用InitScreenCapture获取新的复制对象 // 假设你有全局的设备和复制对象指针,自行调整逻辑 hr = InitScreenCapture(g_pD3DDevice, &g_pOutputDup); return hr; } if (FAILED(hr)) return hr; // 将DXGI资源转换为D3D11纹理 hr = pResource->QueryInterface(IID_PPV_ARGS(ppCapturedTex)); if (SUCCEEDED(hr)) { // 这里可以处理纹理数据,比如复制到你的处理纹理中 pD3DContext->CopyResource(g_pProcessTex, *ppCapturedTex); } // 释放资源 pResource->Release(); pOutputDup->ReleaseFrame(); return hr; }
3. 额外注意事项
- 权限配置:Win10及以上系统,需要在「设置-隐私和安全性-屏幕捕获」中允许你的程序访问屏幕
- DRM内容处理:如果是Netflix这类DRM保护的内容,全屏时无法捕获到有效画面,这是版权限制,没有合法的绕过方法
- 全屏游戏的替代方案:对于禁用桌面复制的独占游戏,可以尝试通过注入D3D11钩子的方式,从游戏的渲染管线中获取帧数据,但这种方法兼容性较差,需要针对不同游戏做适配
内容的提问来源于stack exchange,提问作者Ricardo Alves




