You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动