不会c嘎嘎 2024-09-25 11:37 采纳率: 33.3%
浏览 34
已结题

dxgi截图超时无法正常捕获屏幕

使用dxgi截图的时候如果桌面不发生变化好像就不会正常捕获,用imshow展现出来的窗口会黑屏,这是我的代码,我需要如何改进


#include <dxgi1_2.h>
#include <d3d11.h>
#include <wrl/client.h>
#include <opencv2/opencv.hpp>
#include <iostream>

using Microsoft::WRL::ComPtr;

cv::Mat capture_screen_dxgi() {
    // 初始化 D3D11 设备和上下文
    ComPtr<ID3D11Device> d3dDevice;
    ComPtr<ID3D11DeviceContext> d3dContext;
    D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, nullptr, &d3dContext);

    // 获取 DXGI 设备
    ComPtr<IDXGIDevice> dxgiDevice;
    d3dDevice.As(&dxgiDevice);

    // 获取 DXGI 工厂
    ComPtr<IDXGIAdapter> dxgiAdapter;
    dxgiDevice->GetAdapter(&dxgiAdapter);
    ComPtr<IDXGIFactory2> dxgiFactory;
    dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory);

    // 获取输出(即屏幕)
    ComPtr<IDXGIOutput> dxgiOutput;
    dxgiAdapter->EnumOutputs(0, &dxgiOutput);

    // 获取输出的描述
    DXGI_OUTPUT_DESC outputDesc;
    dxgiOutput->GetDesc(&outputDesc);

    // 创建输出复制(duplication)接口
    ComPtr<IDXGIOutput1> dxgiOutput1;
    dxgiOutput.As(&dxgiOutput1);
    ComPtr<IDXGIOutputDuplication> outputDuplication;
    dxgiOutput1->DuplicateOutput(d3dDevice.Get(), &outputDuplication);

    // 轮询并获取屏幕帧
    DXGI_OUTDUPL_FRAME_INFO frameInfo;
    ComPtr<IDXGIResource> desktopResource;
    HRESULT hr = outputDuplication->AcquireNextFrame(500, &frameInfo, &desktopResource);
    
    if (FAILED(hr)) {
        std::cerr << "Failed to acquire frame from output duplication!" << std::endl;
        return cv::Mat();
    }

    // 获取帧资源
    ComPtr<ID3D11Texture2D> acquiredDesktopImage;
    desktopResource.As(&acquiredDesktopImage);

    // 获取屏幕描述
    D3D11_TEXTURE2D_DESC textureDesc;
    acquiredDesktopImage->GetDesc(&textureDesc);

    // 创建 CPU 访问的纹理
    D3D11_TEXTURE2D_DESC cpuTextureDesc = textureDesc;
    cpuTextureDesc.Usage = D3D11_USAGE_STAGING;
    cpuTextureDesc.BindFlags = 0;
    cpuTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    cpuTextureDesc.MiscFlags = 0;

    ComPtr<ID3D11Texture2D> cpuTexture;
    d3dDevice->CreateTexture2D(&cpuTextureDesc, nullptr, &cpuTexture);

    // 将屏幕图像拷贝到 CPU 访问的纹理中
    d3dContext->CopyResource(cpuTexture.Get(), acquiredDesktopImage.Get());

    // 映射纹理以获取数据
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    d3dContext->Map(cpuTexture.Get(), 0, D3D11_MAP_READ, 0, &mappedResource);

    // 创建 OpenCV Mat 对象
    cv::Mat screenshot(textureDesc.Height, textureDesc.Width, CV_8UC4, mappedResource.pData, mappedResource.RowPitch);

    // 解锁资源
    d3dContext->Unmap(cpuTexture.Get(), 0);
    outputDuplication->ReleaseFrame();

    // 转换为 BGR 格式
    cv::cvtColor(screenshot, screenshot, cv::COLOR_BGRA2BGR);

    return screenshot;
}


  • 写回答

1条回答 默认 最新

  • 科技梦想家 2024-09-25 13:17
    关注

    以下内容由AIGC 及 gpt.4o 科技梦想家编程波士 共同为您回答解决方案、有用望采纳☀️☀️:

    你的代码看起来大体上是正确的,但黑屏问题可能与几个因素有关。首先,确保你在调用 AcquireNextFrame 时,桌面确实有变化。可以尝试在调用之前加一些延时或者确保窗口在前台。其次,确保在创建 CPU 访问的纹理时,textureDesc 的格式支持转换为 BGR。你也可以尝试将 D3D11_TEXTURE2D_DESC 中的 Format 设置为 DXGI_FORMAT_B8G8R8A8_UNORM。最后,确认 OpenCV 窗口使用的是正确的格式,可以尝试先显示原始数据,排查问题。希望这些建议能帮助你进一步深入调试!

    好的,下面是改进后的代码,包含了一些调整和建议,旨在帮助解决黑屏问题:

    
    ```bash
    #include <dxgi1_2.h>
    #include <d3d11.h>
    #include <wrl/client.h>
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <thread> // 用于延时
    
    using Microsoft::WRL::ComPtr;
    
    cv::Mat capture_screen_dxgi() {
        // 初始化 D3D11 设备和上下文
        ComPtr<ID3D11Device> d3dDevice;
        ComPtr<ID3D11DeviceContext> d3dContext;
        D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, nullptr, &d3dContext);
    
        // 获取 DXGI 设备
        ComPtr<IDXGIDevice> dxgiDevice;
        d3dDevice.As(&dxgiDevice);
    
        // 获取 DXGI 工厂
        ComPtr<IDXGIAdapter> dxgiAdapter;
        dxgiDevice->GetAdapter(&dxgiAdapter);
        ComPtr<IDXGIFactory2> dxgiFactory;
        dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory);
    
        // 获取输出(即屏幕)
        ComPtr<IDXGIOutput> dxgiOutput;
        dxgiAdapter->EnumOutputs(0, &dxgiOutput);
    
        // 获取输出的描述
        DXGI_OUTPUT_DESC outputDesc;
        dxgiOutput->GetDesc(&outputDesc);
    
        // 创建输出复制(duplication)接口
        ComPtr<IDXGIOutput1> dxgiOutput1;
        dxgiOutput.As(&dxgiOutput1);
        ComPtr<IDXGIOutputDuplication> outputDuplication;
        dxgiOutput1->DuplicateOutput(d3dDevice.Get(), &outputDuplication);
    
        // 等待屏幕变化,防止黑屏
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
        // 轮询并获取屏幕帧
        DXGI_OUTDUPL_FRAME_INFO frameInfo;
        ComPtr<IDXGIResource> desktopResource;
        HRESULT hr = outputDuplication->AcquireNextFrame(500, &frameInfo, &desktopResource);
        
        if (FAILED(hr)) {
            std::cerr << "Failed to acquire frame from output duplication!" << std::endl;
            return cv::Mat();
        }
    
        // 获取帧资源
        ComPtr<ID3D11Texture2D> acquiredDesktopImage;
        desktopResource.As(&acquiredDesktopImage);
    
        // 获取屏幕描述
        D3D11_TEXTURE2D_DESC textureDesc;
        acquiredDesktopImage->GetDesc(&textureDesc);
    
        // 创建 CPU 访问的纹理
        D3D11_TEXTURE2D_DESC cpuTextureDesc = textureDesc;
        cpuTextureDesc.Usage = D3D11_USAGE_STAGING;
        cpuTextureDesc.BindFlags = 0;
        cpuTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
        cpuTextureDesc.MiscFlags = 0;
    
        ComPtr<ID3D11Texture2D> cpuTexture;
        d3dDevice->CreateTexture2D(&cpuTextureDesc, nullptr, &cpuTexture);
    
        // 将屏幕图像拷贝到 CPU 访问的纹理中
        d3dContext->CopyResource(cpuTexture.Get(), acquiredDesktopImage.Get());
    
        // 映射纹理以获取数据
        D3D11_MAPPED_SUBRESOURCE mappedResource;
        d3dContext->Map(cpuTexture.Get(), 0, D3D11_MAP_READ, 0, &mappedResource);
    
        // 创建 OpenCV Mat 对象
        cv::Mat screenshot(textureDesc.Height, textureDesc.Width, CV_8UC4, mappedResource.pData, mappedResource.RowPitch);
    
        // 解锁资源
        d3dContext->Unmap(cpuTexture.Get(), 0);
        outputDuplication->ReleaseFrame();
    
        // 转换为 BGR 格式
        cv::cvtColor(screenshot, screenshot, cv::COLOR_BGRA2BGR);
    
        return screenshot;
    }
    
    int main() {
        cv::Mat screenshot = capture_screen_dxgi();
        if (!screenshot.empty()) {
            cv::imshow("Screenshot", screenshot);
            cv::waitKey(0); // 等待按键以关闭窗口
        }
        return 0;
    }
    
    
    

    主要改动:
    延时:在获取屏幕帧之前增加了短暂的延时,确保屏幕状态有变化。
    错误处理:保持原有错误处理逻辑,以防获取帧失败。
    主函数:加入了简单的主函数用于测试截图功能。
    你可以运行这个代码并查看截图是否正常。如果仍然有问题,可以进一步检查设备的支持以及其他配置。

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 9月29日
  • 创建了问题 9月25日