qt+gxdi+ffmpeg远程控制(六)

本文介绍了如何在Qt/GxDI/FFmpeg的远程控制环境中,通过D3D11技术设置混合模式来正确显示鼠标透明部分,并优化了渲染结构,包括创建可绑定到管线的组件接口。同时提及了编码器性能问题及其解决方案,可能涉及硬编码以提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

qt+gxdi+ffmpeg远程控制(五)

这次主要吧鼠标绘制出来,废了老劲了...

首先需要设置混合模式,不然鼠标的透明部分会呈现出纯黑色,不能直接将鼠标数据复制到抓取的桌面纹理中。

// 透明
	ID3D11BlendState* blendState;
	D3D11_BLEND_DESC blendDesc = {};
	blendDesc.AlphaToCoverageEnable = FALSE;
	blendDesc.IndependentBlendEnable = FALSE;
	blendDesc.RenderTarget->BlendEnable = TRUE; // 是否开启混合
	blendDesc.RenderTarget->SrcBlend = D3D11_BLEND_SRC_ALPHA; // 将源图的 alpha 作为 src rgb 的混合因子
	blendDesc.RenderTarget->DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // 将源图的 1-alpha 作为 dst rgb 的混合因子
	blendDesc.RenderTarget->BlendOp = D3D11_BLEND_OP_ADD; // 进行相加操作
	blendDesc.RenderTarget->SrcBlendAlpha = D3D11_BLEND_ONE; // 
	blendDesc.RenderTarget->DestBlendAlpha = D3D11_BLEND_ONE;
	blendDesc.RenderTarget->BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendDesc.RenderTarget->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; // 可以写入的位置
	this->m_device->CreateBlendState(&blendDesc, &blendState);
	const FLOAT BlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	this->m_context->OMSetBlendState(blendState, BlendFactor, 0xffffffff);
	SAFE_RELEASE(blendState);

以上是设置混合模式的代码,主要是将鼠标的透明部分正确显示。

需要将鼠标数据读取为单独的纹理,也就是每次渲染两个纹理视图,因此优化了渲染结构。

将所有可绑定到管线的组件统一封装,提供出接口。

class BindAbleInterface {
public:
	BindAbleInterface() = default;
	virtual void bind(ID3D11DeviceContext* _context) = 0;
	virtual ~BindAbleInterface() = default;
};

class IndexBufferBindAble : public BindAbleInterface {
public:
	IndexBufferBindAble(ID3D11Device* _device, const UINT16* _indices, size_t _indicesSize);
	void bind(ID3D11DeviceContext* _context) override;
	~IndexBufferBindAble() override;
private:
	Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer = nullptr;
};

class VertexScalBufferBindAble: public BindAbleInterface {
public:
	__declspec(align(16)) struct {
		__declspec(align(16)) struct {
			float x;
			float y;
			float z;
		} Scal;
		__declspec(align(16)) struct {
			float x;
			float y;
		} Translation;
	} m_psConstant;

	VertexScalBufferBindAble(ID3D11Device* _device, float _x_rate, float _y_rate, float _z_rate, float _x, float _y);
	void bind(ID3D11DeviceContext* _context) override;
	void changeTranslation(ID3D11DeviceContext* _context, float _x, float _y);
	~VertexScalBufferBindAble();
private:
	Microsoft::WRL::ComPtr<ID3D11Buffer> m_ps_constant_buffer = nullptr;
};


void VertexScalBufferBindAble::bind(ID3D11DeviceContext* _context)
{
	_context->VSSetConstantBuffers(0, 1, this->m_ps_constant_buffer.GetAddressOf());
}

void VertexScalBufferBindAble::changeTranslation(ID3D11DeviceContext* _context, float _x, float _y)
{
	m_psConstant.Translation.x = _x;
	m_psConstant.Translation.y = _y;
	D3D11_MAPPED_SUBRESOURCE mapped_resource;
	_context->Map(this->m_ps_constant_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource);
	memcpy(mapped_resource.pData, (void*)&m_psConstant, sizeof(m_psConstant));
	_context->Unmap(this->m_ps_constant_buffer.Get(), 0);
}

VertexScalBufferBindAble::~VertexScalBufferBindAble()
{
}

以上是VertexScalBufferBindAble的例子,给出缩放和平移矩阵,主要给鼠标纹理进行缩放和平移。

修改VertexShader,将变换矩阵乘进去

struct VSOut
{
	float2 tex: TEXCOORD;
	float4 pos : SV_POSITION;
};

struct Scal {
	float x;
	float y;
	float z;
};

struct Translation {
	float x;
	float y;
};

cbuffer Cbuf {
	Scal scal;
	Translation translation;
};

VSOut main_VS(float3 pos : POSITION, float2 tex : TEXCOORD)
{
	VSOut vsout;
	vsout.pos = float4((pos.x * scal.x) + translation.x, (pos.y * scal.y) + translation.y, pos.z * scal.z, 1);
	vsout.tex = tex;
	return vsout;
}

接下来,绘制鼠标,其它逻辑详细可以看源码,这里说一下怎么将鼠标转为纹理

void CursorTextureDrawAble::drawCursor(MyDx11* _myDx11)
{
	CURSORINFO ci = { 0 };
	ci.cbSize = sizeof(ci);
	if (!GetCursorInfo(&ci)) {
		return;
	}
	float offset_x = ci.ptScreenPos.x / this->m_base_x;
	float offset_y = ci.ptScreenPos.y / this->m_base_y;
	offset_x = offset_x * 2 - 1;
	offset_y = 1 - offset_y * 2;
	this->m_vertex_scal_buffer->changeTranslation(this->getContext(_myDx11), offset_x, offset_y);
	if (this->m_cache_cursor == nullptr || this->m_cache_cursor != ci.hCursor) {
		this->m_cache_cursor = ci.hCursor;
	}
	else {
		return;
	}
}

缓存一下已经获取的鼠标句柄,如果没有变化就不需要更改纹理数据了,算一下鼠标的相对位置进行平移变换。

bool CursorTextureDrawAble::copyFromColor(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
	uint8_t* output = nullptr;
	if (GetObject(_icon_info.hbmColor, sizeof(_bmp), &_bmp) == 0) {
		return false;
	}
	unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
	output = new uint8_t[size];
	GetBitmapBits(_icon_info.hbmColor, size, output);
	if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
		this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
	}
	this->copyData(_myDx11, output, size);
	delete[] output;
	return true;
}

bool CursorTextureDrawAble::copyFromMask(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
	long pixels;
	long bottom;
	uint8_t pix_byte;
	bool alpha = false;
	if (GetObject(_icon_info.hbmMask, sizeof(_bmp), &_bmp) == 0) {
		return false;
	}
	uint8_t* output;
	uint8_t* mask;
	unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
	mask = new uint8_t[size];
	GetBitmapBits(_icon_info.hbmMask, size, mask);
	_bmp.bmHeight /= 2;
	pixels = _bmp.bmHeight * _bmp.bmWidth;
	output = new uint8_t[pixels * 4];
	ZeroMemory(output, pixels * 4);
	bottom = _bmp.bmWidthBytes * _bmp.bmHeight;
	for (long i = 0; i < pixels; i++) {
		uint8_t andMask = this->bitToAlpha(mask, i);
		uint8_t xorMask = this->bitToAlpha(mask + bottom, i);
		if (!andMask) {
			// black in the AND mask
			*(uint32_t*)&output[i * 4] =
				!!xorMask ? 0x00FFFFFF /*always white*/
				: 0xFF000000 /*always black*/;
		}
		else {
			// white in the AND mask
			*(uint32_t*)&output[i * 4] =
				!!xorMask ? 0xFFFFFFFF /*source inverted*/
				: 0 /*transparent*/;
		}
	}
	if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
		this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
	}
	this->copyData(_myDx11, output, pixels * 4);
	delete[] output;
	delete[] mask;
	return true;
}

uint8_t CursorTextureDrawAble::bitToAlpha(uint8_t* data, long pixel)
{
	uint8_t pix_byte = data[pixel / 8];
	bool alpha = (pix_byte >> (7 - pixel % 8) & 1) != 0;
	return alpha ? 0xFF : 0;
}

void TextureDrawAble::copyData(MyDx11* _myDx11, uint8_t* _buffer, size_t _size)
{
	D3D11_MAPPED_SUBRESOURCE target_map_res;
	UINT sub_resource = D3D11CalcSubresource(0, 0, 0);
	ID3D11DeviceContext* context = this->getContext(_myDx11);
	HRESULT hr = context->Map(this->m_texture.Get(), sub_resource, D3D11_MAP_WRITE_DISCARD, 0, &target_map_res);
	if (FAILED(hr)) {
		return;
	}
	memcpy_s(target_map_res.pData, _size, _buffer, _size);
	context->Unmap(this->m_texture.Get(), sub_resource);
}

以上是通过鼠标句柄获取到bitmap,再copy到纹理中,参考了obs源码。之前提到当鼠标处于选中文字形状,也就是类似I的形状时获取不到鼠标。其原因在于需要从hbmMask中获取数据。里面的处理逻辑看不太明白,直接拿的obs的源码。

关于编码器性能问题,设置编码线程数可以解决部分问题,但性能依旧不太高。

this->m_encode_pCodecCtx->thread_count = 4;

如果实在不行,考虑使用硬编码方式。

具体详情转到源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值