首页 > 解决方案 > 桌面复制 API 返回空帧

问题描述

我知道已经有一些问题问这个或类似的问题,我深入研究了其中的一些,但没有任何成功。

我尝试使用桌面复制 API 捕获我的显示器的“屏幕截图”并处理它的像素数据。后来我想每秒至少做 30 次,但那是另一种情况。目前,我尝试了microsoft的示例:https ://github.com/microsoftarchive/msdn-code-gallery-microsoft/tree/master/Official%20Windows%20Platform%20Sample/DXGI%20desktop%20duplication%20sample 我成功保存了屏幕图片并使用该代码访问像素数据。

    DirectX::ScratchImage image;
    hr = DirectX::CaptureTexture(m_Device, m_DeviceContext, m_AcquiredDesktopImage, image);
    hr = DirectX::SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS_NONE, L"test.dds");

    uint8_t* pixels;
    pixels = image.GetPixels();

现在我想将示例代码分解为我需要的基本内容。由于我不熟悉 DirectX,我很难做到这一点。我想出了以下代码,它运行时没有错误,但会产生一张空白图片。我在调试模式下检查 hr,我知道这是不好的做法和肮脏的!

int main()
{
    HRESULT hr = S_OK;

    ID3D11Device* m_Device;
    ID3D11DeviceContext* m_DeviceContext;


        // Driver types supported
    D3D_DRIVER_TYPE DriverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT NumDriverTypes = ARRAYSIZE(DriverTypes);

    // Feature levels supported
    D3D_FEATURE_LEVEL FeatureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };
    UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);

    D3D_FEATURE_LEVEL FeatureLevel;

    // Create device
    for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
    {
        hr = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 0, FeatureLevels, NumFeatureLevels,
            D3D11_SDK_VERSION, &m_Device, &FeatureLevel, &m_DeviceContext);
        if (SUCCEEDED(hr))
        {
            // Device creation success, no need to loop anymore
            break;
        }
    }


    IDXGIOutputDuplication* m_DeskDupl;
    IDXGIOutput1* DxgiOutput1 = nullptr;
    IDXGIOutput* DxgiOutput = nullptr;
    IDXGIAdapter* DxgiAdapter = nullptr;
    IDXGIDevice* DxgiDevice = nullptr;
    UINT Output = 0;

    hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));

    hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
    DxgiDevice->Release();
    DxgiDevice = nullptr;

    hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
    DxgiAdapter->Release();
    DxgiAdapter = nullptr;

    hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
    DxgiOutput->Release();
    DxgiOutput = nullptr;

    hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);




    IDXGIResource* DesktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;

    hr = m_DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);

    ID3D11Texture2D* m_AcquiredDesktopImage;

    hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&m_AcquiredDesktopImage));
    DesktopResource->Release();
    DesktopResource = nullptr;

    DirectX::ScratchImage image;
    hr = DirectX::CaptureTexture(m_Device, m_DeviceContext, m_AcquiredDesktopImage, image);
    hr = DirectX::SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS_NONE, L"test.dds");

    uint8_t* pixels;
    pixels = image.GetPixels();


    hr = m_DeskDupl->ReleaseFrame();
}

谁能给我一个提示,这段代码有什么问题?

编辑:刚刚找到下面的代码片段并将其集成到我的代码中。现在它起作用了!经验教训:-) 实际输出/处理小时!-) AcquireNextFrame 可能在第一次尝试时不起作用(?)

我可能会用更好的代码和功能循环再次更新这篇文章。

    int lTryCount = 4;

    do
    {
        Sleep(100);

        hr = m_DeskDupl->AcquireNextFrame(250, &FrameInfo, &DesktopResource);

        if (SUCCEEDED(hr))
            break;

        if (hr == DXGI_ERROR_WAIT_TIMEOUT)
        {
            continue;
        }
        else if (FAILED(hr))
            break;

    } while (--lTryCount > 0);

标签: directxdesktop-duplication

解决方案


AcquireNextFrame允许返回空资源(纹理),因为它在桌面图像更改或与指针相关的更改时返回。

AcquireNextFrame 在操作系统更新桌面位图图像或更改硬件指针的形状或位置时获取新的桌面框架。

当您开始帧采集时,您显然很快就会获得第一个桌面图像,但您也可以在图像之前有一些指针通知。

你不应该限制自己尝试 4 次,也不需要在循环中睡觉。继续轮询图像。为了避免死循环,跟踪循环中花费的总时间并将其限制为例如一秒更有意义。

也可以看看:


推荐阅读