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

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

标题写错了居然,

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

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

先说第一个:

本人下载的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

猜你喜欢

转载自blog.csdn.net/xjb2006/article/details/107079312