DXGI抓屏优化扩展:鼠标功能+数据获取+多显示器捕获(屏幕共享源码)

    技术2022-07-12  73

    先列个标题,争取这2天写完。。。

    标题写错了居然,

    我们一般要实现某个功能,首先希望能找到对应的DEMO,比如我们做屏幕共享,在WIN10下,首先想到的就是DXGI技术,帧率和效率是非常不错的,这里不讲性能,讲下怎么扩展某些功能:

    鼠标功能:数据获取多显示器需求(多屏,副显,扩展屏说法很多)

    先说第一个:

    本人下载的DEMO不含鼠标功能,好的,我们加上,直接上代码就是这么简单粗暴:

    D3D11_MAPPED_SUBRESOURCE mapdesc; HRESULT hRes = m_hContext->Map((ID3D11Texture2D *)hNewDesktopImage, m_Subresource, D3D11_MAP_READ_WRITE, 0, &mapdesc); if (FAILED(hRes)) { return m_pBuf; } if (m_bHaveCursor)//画鼠标 hRes = DrawCursor2(mapdesc, frameDescriptor, FrameInfo); m_hContext->Unmap(hAcquiredDesktopImage, m_Subresource);

    上面这段代码可以控制是否画鼠标,下面是DrawCursor2的实现函数:

    HRESULT VideoDXGICaptor::DrawCursor2(D3D11_MAPPED_SUBRESOURCE mapdesc, D3D11_TEXTURE2D_DESC desc, DXGI_OUTDUPL_FRAME_INFO frameInfo) { HRESULT hRes = S_OK; bool bShowCursor = true; if (mapdesc.pData) { CURSORINFO ci; memset(&ci, 0, sizeof(ci)); ci.cbSize = sizeof(ci); if (GetCursorInfo(&ci)) { memcpy(&m_CursorPos, &ci.ptScreenPos, sizeof(m_CursorPos)); if (ci.flags & CURSOR_SHOWING) { if (ci.hCursor != m_hCurrentCursor) // re-get cursor data { HICON hIcon = CopyIcon(ci.hCursor); m_hCurrentCursor = ci.hCursor; free(m_CursorData); m_CursorData = NULL; if (hIcon) { ICONINFO ii; if (GetIconInfo(hIcon, &ii)) { xHotspot = int(ii.xHotspot); yHotspot = int(ii.yHotspot); m_CursorData = GetCursorData(hIcon, ii, m_CursorWidth, m_CursorHeight, m_CursorPitch); DeleteObject(ii.hbmColor); DeleteObject(ii.hbmMask); } DestroyIcon(hIcon); } } } else { bShowCursor = false; } } if (m_CursorData && bShowCursor) { // Not supporting mono and masked pointers at the moment //printf("Drawing pointer at %d %d\n", data->PointerPosition.Position.x, data->PointerPosition.Position.y); const int ptrx = m_CursorPos.x - xHotspot; const int ptry = m_CursorPos.y - yHotspot; uint8_t* ptr = m_CursorData; uint8_t* dst; // ### Should really do the blending on the GPU (Using DirectX) rather than using SSE2 on the CPU const int ptrw = min(m_CursorWidth, desc.Width - ptrx); for (unsigned int y = 0; y < m_CursorHeight; ++y) { if (y + ptry >= desc.Height) break; dst = static_cast<uint8_t*>(mapdesc.pData) + (((y + ptry) * mapdesc.RowPitch) + (ptrx * 4)); //memcpy(dst, ptr, data->PointerShape.Width * 4); ARGBBlendRow_SSE2(ptr, dst, dst, ptrw); ptr += m_CursorPitch; } } } return hRes; }

     完成!收工!完美!

    第二个问题:数据的获取,我们要从GPU获取数据到CPU内存:

    原DEMO的例子是直接将数据大小默认为宽X高X4,这样对于大多数分辨率可能没问题,但是对于一些特殊分辨率比如1366X768,这样是有问题的,得到的数据是错位的。这牵涉到数据对齐问题,一定要注意。

    原始:memcpy((BYTE*)m_pBuf, mappedRect.pBits, m_dxgiOutDesc.DesktopCoordinates.right * m_dxgiOutDesc.DesktopCoordinates.bottom * 4);

    修改为:

            int nImagePitch = m_iWidth * 4;

            for (int i = 0; i < m_iHeight; i++)         {             memcpy(m_pBuf + i * nImagePitch, mappedRect.pBits + i * mapdesc.RowPitch, mapdesc.RowPitch);         }

    OK!最后一个问题,多显示器需求,共享主屏幕都可以的,但是也有接副显的需求啊,这方面的资料比较难找。遇到问题我一般会先思考,能自己解决最好,比如第二个问题,因为接触类似问题多了,闭着眼睛也知道什么原因。第一个问题原来做GDI的时候也遇到过,所以稍微查下资料也可以解决。第3个比较难找,自己也不是很熟悉,但是问题始终还是要解决的,具体解决过程就不说了,反正现在知道了,呵呵。

    先贴代码:

    //定义一个数据结构,这里面是每个DXGI的输出和对应的适配器(显示器),没理解错的话 struct DxgiData { IDXGIAdapter* pAdapter; IDXGIOutput *pOutput; }; std::vector<DxgiData> m_vOutputs; int Enum() { IDXGIFactory1* pFactory1; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory1)); if (FAILED(hr)) { return 0; } for (UINT i = 0;; i++) { IDXGIAdapter1* pAdapter1 = nullptr; hr = pFactory1->EnumAdapters1(i, &pAdapter1); if (hr == DXGI_ERROR_NOT_FOUND) { // no more adapters break; } if (FAILED(hr)) { return 0; } DXGI_ADAPTER_DESC1 desc; hr = pAdapter1->GetDesc1(&desc); if (FAILED(hr)) { return 0; } desc.Description; for (UINT j = 0;; j++) { IDXGIOutput *pOutput = nullptr; HRESULT hr = pAdapter1->EnumOutputs(j, &pOutput); if (hr == DXGI_ERROR_NOT_FOUND) { // no more outputs break; } if (FAILED(hr)) { return 0; } DXGI_OUTPUT_DESC desc; hr = pOutput->GetDesc(&desc); if (FAILED(hr)) { return 0; } desc.DeviceName; desc.DesktopCoordinates.left; desc.DesktopCoordinates.top; int width=desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; int height=desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; DxgiData data; data.pAdapter = pAdapter1; data.pOutput = pOutput; m_vOutputs.push_back(data); } } return m_vOutputs.size(); }

    上面是枚举显示器适配器信息:

    下面贴初始化调用代码:

    HRESULT hr = S_OK; if (m_bInit) { return FALSE; } int nCount=Enum(); if (nCount <= 0) return FALSE; m_iWidth = m_iHeight = 0; // 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 }; // // Get output // INT nOutput = 0; DxgiData data; IDXGIAdapter *pAdapter = NULL; IDXGIOutput *hDxgiOutput = NULL; int nSize = m_vOutputs.size(); if (m_nDisPlay<0||m_nDisPlay >= nSize) m_nDisPlay = 0; data = m_vOutputs.at(m_nDisPlay); hDxgiOutput = data.pOutput; pAdapter = data.pAdapter; hDxgiOutput->GetDesc(&m_dxgiOutDesc); UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels); for (int i = 0; i < NumFeatureLevels; i++) { D3D_FEATURE_LEVEL FeatureLevel = FeatureLevels[i]; hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, 0, 0, D3D11_SDK_VERSION, &m_hDevice, &FeatureLevel, &m_hContext); if (SUCCEEDED(hr)) { break; } } // // 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; } 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_Subresource = D3D11CalcSubresource(0, 0, 0); AttatchToThread(); // 初始化成功 m_bInit = TRUE; return TRUE;

    好了,到此为止,我也无话可说,简直就是完美,没人比我更懂DXGI了(听上去好熟悉,逗逼川普的名言)。。。

    m_nDisPlay随意指定,看看效果吧

    本人QQ35744025,寻求合作随时骚扰即可

    最后附上一个GDI多屏的下载链接:

    https://download.csdn.net/download/xjb2006/12570425

     

    Processed: 0.023, SQL: 9