zoukankan      html  css  js  c++  java
  • DXGI屏幕捕捉

    概述

      很多软件都需要屏幕捕捉功能,在软件中实现屏幕捕捉也不是难事,在微软Windows平台,有很多截屏的方法,例如:BitBlt、Mirror driver、 GDI hook、DirectX、DWM/Dxgi hook、Desktop Duplication与GetWindowDC 等方法,但大多效率不高,效率高的 Mirror driver技术只能用于XP等老系统,在Windows8 与Windows 10 上似乎已经失效,Windows8以后微软引入了一套新的接口,叫“Desktop Duplication API”,应用程序可以通过这套API访问桌面数据。
          Desktop Duplication API是通过Microsoft DirectX Graphics Infrastructure (DXGI)来提供桌面图像的,速度非常快。DXGI是通过GPU实现的,因此cpu占用率很低,性能非常高。 Duplication API获取到的桌面数据,不管显示模式如何设置,都永远是32位RGBA数据,这就给屏幕捕捉带来了很大的方便性,不再需要考虑各种显示模式的问题了。


    要实现DXGI屏幕捕捉,基本流程如下:

    1)创建D3DDevice;
    2)通过一系列接口获取路径,获取到IDXGIOutputDuplication接口;
    3)调用AcquireNextFrame,获取当前桌面数据,保存在IDXGIResource中;
    4)把数据从GPU映射到内存中拷贝需要的数据到自己的buffer里。

    其中,获取到IDXGIOutputDuplication接口,是通过如下路径:
    IDXGIDevice --> IDXGIAdapter --> IDXGIOutput --> IDXGIOutput1 --> IDXGIOutputDuplication

    真实实现DXGI屏幕捕捉的代码如下:

    //
    //DXGICapture.h
    //
     
    #include <d3d11.h>
    #include <dxgi1_2.h>
     
    class VideoDXGICaptor
    {
    public:
        VideoDXGICaptor();
        ~VideoDXGICaptor();
     
    public:
        BOOL Init();
        VOID Deinit();
     
    public:
        virtual BOOL CaptureImage(RECT &rect, void *pData, INT &nLen);
        virtual BOOL CaptureImage(void *pData, INT &nLen);
        virtual BOOL ResetDevice();
     
    private:
        BOOL  AttatchToThread(VOID);
        BOOL  QueryFrame(void *pImgData, INT &nImgSize);
        BOOL  QueryFrame(void *pImgData, INT &nImgSize, int z);
     
    private:
        IDXGIResource *zhDesktopResource;
        DXGI_OUTDUPL_FRAME_INFO zFrameInfo;
        ID3D11Texture2D *zhAcquiredDesktopImage;
        IDXGISurface *zhStagingSurf;
     
    private:
        BOOL                    m_bInit;
        int                     m_iWidth, m_iHeight;
     
        ID3D11Device           *m_hDevice;
        ID3D11DeviceContext    *m_hContext;
     
        IDXGIOutputDuplication *m_hDeskDupl;
        DXGI_OUTPUT_DESC        m_dxgiOutDesc;
    };
     
     
    //
    //DXGICapture.cpp
    //
     
    #include "stdafx.h"
    #include "DXGICaptor.h"
    #include <windows.h>
    #include <gdiplus.h>
     
    #pragma comment(lib, "d3d11.lib")
    #pragma comment(lib, "dxgi.lib")
     
    #define RESET_OBJECT(obj) { if(obj) obj->Release(); obj = NULL; }
    static BOOL g_bAttach = FALSE;
     
    VideoDXGICaptor::VideoDXGICaptor()
    {
        m_bInit = FALSE;
     
        m_hDevice = NULL;
        m_hContext = NULL;
        m_hDeskDupl = NULL;
     
        ZeroMemory(&m_dxgiOutDesc, sizeof(m_dxgiOutDesc));
    }
    VideoDXGICaptor::~VideoDXGICaptor()
    {
        Deinit();
    }
    BOOL VideoDXGICaptor::Init()
    {
        HRESULT hr = S_OK;
     
        if (m_bInit)
        {
            return FALSE;
        }
     
        // 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 D3D device
        //
        for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
        {
            hr = D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &m_hDevice, &FeatureLevel, &m_hContext);
            if (SUCCEEDED(hr))
            {
                break;
            }
        }
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // Get DXGI device
        //
        IDXGIDevice *hDxgiDevice = NULL;
        hr = m_hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&hDxgiDevice));
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // Get DXGI adapter
        //
        IDXGIAdapter *hDxgiAdapter = NULL;
        hr = hDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&hDxgiAdapter));
        RESET_OBJECT(hDxgiDevice);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // Get output
        //
        INT nOutput = 0;
        IDXGIOutput *hDxgiOutput = NULL;
        hr = hDxgiAdapter->EnumOutputs(nOutput, &hDxgiOutput);
        RESET_OBJECT(hDxgiAdapter);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // get output description struct
        //
        hDxgiOutput->GetDesc(&m_dxgiOutDesc);
        
        //
        // QI for Output 1
        //
        IDXGIOutput1 *hDxgiOutput1 = NULL;
        hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast<void**>(&hDxgiOutput1));
        RESET_OBJECT(hDxgiOutput);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // Create desktop duplication
        //
        hr = hDxgiOutput1->DuplicateOutput(m_hDevice, &m_hDeskDupl);
        RESET_OBJECT(hDxgiOutput1);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        // 初始化成功
        m_bInit = TRUE;
        return TRUE;
    // #else
        // 小于vs2012,此功能不能实现
        return FALSE;
    // #endif
    }
    VOID VideoDXGICaptor::Deinit()
    {
        if (!m_bInit)
        {
            return;
        }
     
        m_bInit = FALSE;
     
        if (m_hDeskDupl)
        {
            m_hDeskDupl->Release();
            m_hDeskDupl = NULL;
        }
     
        if (m_hDevice)
        {
            m_hDevice->Release();
            m_hDevice = NULL;
        }
     
        if (m_hContext)
        {
            m_hContext->Release();
            m_hContext = NULL;
        }
    // #endif
    }
    BOOL VideoDXGICaptor::AttatchToThread(VOID)
    {
        if (g_bAttach)
        {
            return TRUE;
        }
        
        HDESK hCurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);
        if (!hCurrentDesktop)
        {
            return FALSE;
        }
     
        // Attach desktop to this thread
        BOOL bDesktopAttached = SetThreadDesktop(hCurrentDesktop);
        CloseDesktop(hCurrentDesktop);
        hCurrentDesktop = NULL;
     
        g_bAttach = TRUE;
     
        return bDesktopAttached;
    }
     
    BOOL VideoDXGICaptor::CaptureImage(RECT &rect, void *pData, INT &nLen)
    {
        return QueryFrame(pData, nLen);
    }
    BOOL VideoDXGICaptor::CaptureImage(void *pData, INT &nLen)
    {
        return QueryFrame(pData, nLen);
    }
    BOOL VideoDXGICaptor::ResetDevice()
    {
        Deinit();
        return Init();
    }
    BOOL VideoDXGICaptor::QueryFrame(void *pImgData, INT &nImgSize)
    {
        if (!m_bInit || !AttatchToThread())
        {
            return FALSE;
        }
     
        nImgSize = 0;
     
        IDXGIResource *hDesktopResource = NULL;
        DXGI_OUTDUPL_FRAME_INFO FrameInfo;
        HRESULT hr = m_hDeskDupl->AcquireNextFrame(0, &FrameInfo, &hDesktopResource);
        if (FAILED(hr))
        {
            //
            // 在一些win10的系统上,如果桌面没有变化的情况下,;
            // 这里会发生超时现象,但是这并不是发生了错误,而是系统优化了刷新动作导致的。;
            // 所以,这里没必要返回FALSE,返回不带任何数据的TRUE即可;
            //
            return TRUE;
        }
     
        //
        // query next frame staging buffer
        //
        ID3D11Texture2D *hAcquiredDesktopImage = NULL;
        hr = hDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&hAcquiredDesktopImage));
        RESET_OBJECT(hDesktopResource);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // copy old description
        //
        D3D11_TEXTURE2D_DESC frameDescriptor;
        hAcquiredDesktopImage->GetDesc(&frameDescriptor);
     
        //
        // create a new staging buffer for fill frame image
        //
        ID3D11Texture2D *hNewDesktopImage = NULL;
        frameDescriptor.Usage = D3D11_USAGE_STAGING;
        frameDescriptor.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
        frameDescriptor.BindFlags = 0;
        frameDescriptor.MiscFlags = 0;
        frameDescriptor.MipLevels = 1;
        frameDescriptor.ArraySize = 1;
        frameDescriptor.SampleDesc.Count = 1;
        hr = m_hDevice->CreateTexture2D(&frameDescriptor, NULL, &hNewDesktopImage);
        if (FAILED(hr))
        {
            RESET_OBJECT(hAcquiredDesktopImage);
            m_hDeskDupl->ReleaseFrame();
            return FALSE;
        }
     
        //
        // copy next staging buffer to new staging buffer
        //
        m_hContext->CopyResource(hNewDesktopImage, hAcquiredDesktopImage);
     
        RESET_OBJECT(hAcquiredDesktopImage);
        m_hDeskDupl->ReleaseFrame();
     
        //
        // create staging buffer for map bits
        //
        IDXGISurface *hStagingSurf = NULL;
        hr = hNewDesktopImage->QueryInterface(__uuidof(IDXGISurface), (void **)(&hStagingSurf));
        RESET_OBJECT(hNewDesktopImage);
        if (FAILED(hr))
        {
            return FALSE;
        }
     
        //
        // copy bits to user space
        //
        DXGI_MAPPED_RECT mappedRect;
        hr = hStagingSurf->Map(&mappedRect, DXGI_MAP_READ);
        if (SUCCEEDED(hr))
        {
            // nImgSize = GetWidth() * GetHeight() * 3;
            // PrepareBGR24From32(mappedRect.pBits, (BYTE*)pImgData, m_dxgiOutDesc.DesktopCoordinates);
            // mappedRect.pBits;
            // am_dxgiOutDesc.DesktopCoordinates;
            memcpy((BYTE*)pImgData, mappedRect.pBits, m_dxgiOutDesc.DesktopCoordinates.right * m_dxgiOutDesc.DesktopCoordinates.bottom * 4);
            hStagingSurf->Unmap();
        }
     
        RESET_OBJECT(hStagingSurf);
        return SUCCEEDED(hr);
    }
  • 相关阅读:
    NTC温度检测程序(转)
    基于GPS的经纬度、方位角、距离、航向计算及基于单目视觉的距离计算!
    GPS速度和航向计算(转)
    NOR、 NAND、Raw Flash和 Managed Flash的区别(转)
    FreeModbus V1.6 主机使用说明(转)
    只要单片机具有真正唯一ID,就可以让加密坚不可摧(转)
    Java 给Word添加数字签名
    Java 获取Word中指定图片的坐标位置
    C#/VB.NET 自定义PPT动画路径
    在线编辑Excel——插入图表
  • 原文地址:https://www.cnblogs.com/mrguoguo/p/15528627.html
Copyright © 2011-2022 走看看