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

截图代码

bool Monitor::get()
{
	DXGI_OUTDUPL_FRAME_INFO info;
	IDXGIResource* desktop = nullptr;
	HRESULT hr = this->m_duplication->AcquireNextFrame(0, &info, &desktop);
	if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
		return false;
	}

	if (this->m_image != nullptr) {
		this->m_image->Release();
		this->m_image = nullptr;
	}

	hr = desktop->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&this->m_image));
	if (FAILED(hr)) {
		desktop->Release();
		this->m_duplication->ReleaseFrame();
		return false;
	}
	/*if (!this->copy_frame_data(this->m_image, _buffer, _size)) {
		desktop->Release();
		this->m_duplication->ReleaseFrame();
		return false;
	}*/
	desktop->Release();
	desktop = nullptr;
	if (FAILED(hr)) {
		return false;
	}
	return true;
}

以上是gxdi截图流程,前期需要做些工作拿到显示器adapter,根据adapter创建dxdevice。  

bool Monitor::copy_frame_data(ID3D11Texture2D* _texture, uint8_t* _buffer, size_t _size)
{
	D3D11_TEXTURE2D_DESC desc;
	_texture->GetDesc(&desc);

	ID3D11Texture2D* _target = nullptr;
	D3D11_TEXTURE2D_DESC target_desc;
	memcpy_s(&target_desc, sizeof(target_desc), &desc, sizeof(desc));
	target_desc.Usage = D3D11_USAGE_STAGING;
	target_desc.BindFlags = 0;
	target_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	target_desc.MiscFlags = 0;
	HRESULT hr = this->m_device->CreateTexture2D(&target_desc, nullptr, &_target);
	if (FAILED(hr)) {
		return false;
	}
	this->m_context->CopyResource(_target, _texture);
	D3D11_MAPPED_SUBRESOURCE target_map_res;
	UINT subresource = D3D11CalcSubresource(0, 0, 0);
	hr = this->m_context->Map(_target, subresource, D3D11_MAP_READ, 0, &target_map_res);
	if (FAILED(hr)) {
		return false;
	}
	memcpy_s(_buffer, _size, target_map_res.pData, _size);
	this->m_context->Unmap(_target, subresource);
	_target->Release();
	this->m_duplication->ReleaseFrame();
	return true;
}

以上将截图所得的纹理拷贝出去,这些都是常规写法,网络到处是源码。

帧数控制及计算

double Timer::nextFps() const {
	const long long target = mulDiv64(this->m_last + this->m_interval, this->m_clock_freq->QuadPart);
	LARGE_INTEGER now;
	QueryPerformanceCounter(&now);
	const long long wait = (((target - now.QuadPart) * 1000.0) / this->m_clock_freq->QuadPart);
	if (now.QuadPart < target) {
		
		if (wait > 1) {
			Sleep(wait - 1);
		}
		for (;;) {
			QueryPerformanceCounter(&now);
			if (now.QuadPart >= target) {
				break;
			}

			YieldProcessor();
		}
		return this->m_fps;
	}
	return this->m_fps / (1 - wait * this->m_fps/1000.0);
}

void Timer::mark()
{
	this->m_last = this->now();
}

long long Timer::now() const {
	return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
}

这里参考了obs的帧数控制,now()方法采用chrono库获取时间,帧数控制函数中采用QueryPerformanceCounter获取时间,结合obs帧数控制源码是可以正确运行的。  

如果now()方法也是用QueryPerformanceCounter获取,计算出来结果不正确。具体obs的last_time那块每太理解,这里贴一下。

const uint64_t udiff = os_gettime_ns() - cur_time;
int64_t diff;
memcpy(&diff, &udiff, sizeof(diff));
const uint64_t clamped_diff = (diff > (int64_t)interval_ns)
						      ? (uint64_t)diff
						      : interval_ns;
count = (int)(clamped_diff / interval_ns);
*p_time = cur_time + interval_ns * count;

uint64_t os_gettime_ns(void)
{
	LARGE_INTEGER current_time;
	QueryPerformanceCounter(&current_time);
	return util_mul_div64(current_time.QuadPart, 1000000000,
			      get_clockfreq());
}

它其实gettime_ns是把QueryPerformanceCounter的时间转换出去(转换后位数与chrono库获取的时间位数相同),在帧数控制计算的时候再转换回来,不太理解这一步的作用。

绘制鼠标及渲染

将纹理转为Qimage进行绘制及渲染

void MainWindow::drawCursor(QImage* _image)
{
	QPainter painter(_image);
	CURSORINFO cursorInfo;
	memset(&cursorInfo, 0, sizeof(cursorInfo));
	cursorInfo.cbSize = sizeof(cursorInfo);
	if (GetCursorInfo(&cursorInfo)) {
		HICON hIcon = CopyIcon(cursorInfo.hCursor);
		if (hIcon) {
			ICONINFO iconInfo;
			if (GetIconInfo(hIcon, &iconInfo)) {
				this->m_cursorImage = QImage::fromHBITMAP(iconInfo.hbmColor);
				this->m_cursorPixmap = QPixmap::fromImage(this->m_cursorImage);
				this->m_cursorPixmap.setMask(QBitmap::fromImage(QImage::fromHBITMAP(iconInfo.hbmMask)));
				DeleteObject(iconInfo.hbmColor);
				DeleteObject(iconInfo.hbmMask);
			}
			painter.drawPixmap(cursorInfo.ptScreenPos.x, cursorInfo.ptScreenPos.y, this->m_cursorPixmap);
		}
	}
}

void PlayWindow::showMonitor(QImage& image)
{
	QPixmap pixmap = QPixmap::fromImage(image.scaled(this->m_imageLabel->width(), this->m_imageLabel->height(), Qt::IgnoreAspectRatio));
	this->m_imageLabel->setPixmap(pixmap);
}

绘制鼠标后将图片转为pixmap放入label中既可,此处有个bug。鼠标在可选中文字上的形状,就是类似于大写i的形状,绘制不出来,不确定是不是mask的原因,也可能是应用窗口太小没看到。  

但这不是重点,考虑不转为QImage,参考obs源码,还在研究。

性能测试

单纯截图性能达到要求,可以稳定60,经测试144也可以基本稳定。

 如果在截图线程中加入渲染,则无法稳定60。

单纯渲染性能测试也是,几乎稳定不到60帧。

 

 后续需要考虑直接采用dx11渲染实时显示组件。

另外,编码线程性能严重不足。

 编码参数设置:

this->m_encode_pCodecCtx = avcodec_alloc_context3(m_encode_pCodec);
	this->m_encode_pCodecCtx->codec_id = AV_CODEC_ID_H264;
	// this->m_encode_pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	this->m_encode_pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
	this->m_encode_pCodecCtx->bit_rate = 25000 * 1000;
	this->m_encode_pCodecCtx->width = width;
	this->m_encode_pCodecCtx->height = height;
	this->m_encode_pCodecCtx->time_base.num = 1;
	this->m_encode_pCodecCtx->time_base.den = fps;
	this->m_encode_pCodecCtx->framerate.num = fps;
	this->m_encode_pCodecCtx->framerate.den = 1;
	this->m_encode_pCodecCtx->gop_size = 10;
av_dict_set(&output_param, "preset", "veryfast", 0);
	// av_dict_set(&output_param, "preset", "ultrafast", 0);
	av_dict_set(&output_param, "tune", "zerolatency", 0);
	av_dict_set(&output_param, "profile", "Main", 0);

这里首先尝试软编,性能无法稳定60帧,不太清楚远控软件144帧是怎么达到的,难道不需要编码压缩直接传?  

首先还是需要解决渲染性能问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值