DXGI抓屏优化扩展:GPU硬件编码保存文件即录像为MP4和FLV,外加麦克风+计算机声音

DXGI抓屏优化扩展:GPU硬件编码保存文件即录像为MP4和FLV,外加麦克风+计算机声音

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

前面一篇文章已经介绍和实现了以下功能:

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

写这篇文章的目的在于继续扩大其高性能应用,我们继续扩展以下功能:

  1. 硬件GPU编码
  2. 录像同时保存为MP4和FLV
  3. 录像时同时预览视频(D3D显示)
  4. 声音输入多种选择:麦克风,计算机声音,麦克风+计算机声音混合

先说第一个:

DXGI如此性能优秀的截屏方法,如果我们再用H264软件编码,是不是有点暴殄天物

(拼音是bào tiǎn tiān wù),既然可以用dxgi抓屏,也可以用显卡加速进行编码,目前代码支持INTEL的QSV硬件加速和英伟达的CUDA硬件加速编码,当然也可以选择H264软编码,前者和后者主要还是性能上的差异,经过硬件加速的H264编码比软件编码更快,缺点是兼容性不是很好,不过既然都支持DXGI了,肯定也有显卡编码加速的支持。

好的,我们加上吧,直接上代码就是这么简单粗暴:

bool CFFFindEncoder::Find(char* szName)
{
	//MessageBox(0,L"1",0,0);
	AVCodecContext *c= NULL;
    //AVFrame *frame;
    AVPacket *pkt;
	AVCodec *codec=avcodec_find_encoder_by_name(szName);
	if(codec==0)
		return false;
	//MessageBox(0,L"2",0,0);
    c = avcodec_alloc_context3(codec);
    if (!c) 
	{
		return false;
    }
	//MessageBox(0,L"3",0,0);
    pkt = av_packet_alloc();
    if (!pkt)
	{
		return false;
    }
    c->bit_rate = 8000000;
    c->width = 1920;
    c->height = 1080;
	c->time_base.num=1;
    c->time_base.den=25;
	c->framerate.num=25;
    c->framerate.den=1;
	
    c->gop_size = 10;
    c->max_b_frames = 0;
    c->pix_fmt = AV_PIX_FMT_NV12;//AV_PIX_FMT_YUV420P;

    if (codec->id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);

    int ret = avcodec_open2(c, codec, NULL);
    if (ret < 0) {
		return false;
    }
	//MessageBox(0,L"4",0,0);

    avcodec_free_context(&c);
	av_packet_free(&pkt);
	return true;
}

以上代码是判断是否支持硬件编码:

调用代码为:

 

	int bOK0=0;//enc.Find(L"opus");
	char* Encoder[4]={"h264_qsv","h264_nvenc","libx264","h264_amf"};
	for(int i=0;i<4;i++)
	{
		char* szEncoder=Encoder[i];
		bOK0=enc.Find(szEncoder);//hevc_nvenc  h264_qsv
		if(bOK0)
		{
			m_nEncoder=i;
			PCHAR szA=Encoder[i];
			m_cx=(m_cx/16)*16;
			m_cy=(m_cy==768?768:m_cy);
			enc.Init(szA,m_cx ,m_cy);
			break;
		}
	}

如上面代码所示, h264_qsv显卡为intel GPU,h264_nvenc显卡为英伟达 GPU,libx264为软件编码,h264_amf还没测试,下次买个AMD的笔记本测试,不过代价挺大,哎,开发也不容易啊!

编码器初始化后就可以编码图像数据(YUV)为视频数据(H264)了,dxgi输出数据为RGB32,我们需要转换为NV12或者YV12,一般用libyuv进行转换效率更高。来吧,上代码:

static int ARGBToYv12(uint8* src_frame,uint8* dst_frame,int width,int height)	
{			
	uint8* yplane= dst_frame;		
	uint8* uplane= dst_frame	+ width	* height;		
	uint8* vplane= uplane+	(width	* height/4);		
	int n = libyuv::ARGBToI420(src_frame,width*4,yplane, width,uplane, width/2,vplane, width/2, width, height);		
	return n;        
}

static int ARGBToNv12(uint8* src_frame,uint8* dst_frame,int width,int height)	
{			
	uint8* yplane= dst_frame;		
	uint8* uplane= dst_frame	+ width	* height;		
	uint8* vplane= uplane+	(width	* height/4);		
	int n = libyuv::ARGBToNV12(src_frame,width*4,yplane, width,uplane, width, width, height);		
	return n;        
}

这个颜色空间转换速度很NICE,要做就做最好,这是我的原则! 

先写到这里,下次继续写。。。尽请期待!QQ35744025联系我,美女优先

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是使用Qt和DXGI进行屏幕截图并保存的示例代码: ```c++ #include <QCoreApplication> #include <QImage> #include <QFile> #include <QDebug> #include <d3d11.h> #include <dxgi1_2.h> // Helper function to check HRESULTs #define CHECK_HR(hr, msg) \ if (FAILED(hr)) { \ qDebug() << msg << "failed with HRESULT" << hr; \ return false; \ } bool saveScreenshot(const QString& filePath) { HRESULT hr = S_OK; // Create DXGI factory IDXGIFactory1* pFactory = nullptr; hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory); CHECK_HR(hr, "Failed to create DXGI factory"); // Get primary adapter IDXGIAdapter1* pAdapter = nullptr; hr = pFactory->EnumAdapters1(0, &pAdapter); CHECK_HR(hr, "Failed to get primary adapter"); // Create D3D device and swap chain D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; D3D_FEATURE_LEVEL featureLevels[] = { featureLevel }; DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; swapChainDesc.BufferCount = 1; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = GetDesktopWindow(); swapChainDesc.SampleDesc.Count = 1; swapChainDesc.Windowed = TRUE; ID3D11Device* pDevice = nullptr; ID3D11DeviceContext* pContext = nullptr; IDXGISwapChain* pSwapChain = nullptr; hr = D3D11CreateDeviceAndSwapChain(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, &featureLevel, &pContext); CHECK_HR(hr, "Failed to create D3D device and swap chain"); // Get back buffer texture ID3D11Texture2D* pBackBuffer = nullptr; hr = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); CHECK_HR(hr, "Failed to get back buffer texture"); // Create a CPU-readable texture D3D11_TEXTURE2D_DESC textureDesc = { 0 }; textureDesc.Width = GetSystemMetrics(SM_CXSCREEN); textureDesc.Height = GetSystemMetrics(SM_CYSCREEN); textureDesc.MipLevels = 1; textureDesc.ArraySize = 1; textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; textureDesc.SampleDesc.Count = 1; textureDesc.Usage = D3D11_USAGE_STAGING; textureDesc.BindFlags = 0; textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ID3D11Texture2D* pTexture = nullptr; hr = pDevice->CreateTexture2D(&textureDesc, NULL, &pTexture); CHECK_HR(hr, "Failed to create CPU-readable texture"); // Copy back buffer to CPU-readable texture pContext->CopyResource(pTexture, pBackBuffer); // Read texture data D3D11_MAPPED_SUBRESOURCE mappedResource; hr = pContext->Map(pTexture, 0, D3D11_MAP_READ, 0, &mappedResource); CHECK_HR(hr, "Failed to map texture"); QImage image(mappedResource.pData, textureDesc.Width, textureDesc.Height, QImage::Format_RGB32); pContext->Unmap(pTexture, 0); // Save image to file if (!image.save(filePath)) { qDebug() << "Failed to save screenshot to" << filePath; return false; } // Cleanup pContext->Release(); pDevice->Release(); pSwapChain->Release(); pAdapter->Release(); pFactory->Release(); pTexture->Release(); pBackBuffer->Release(); return true; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); if (saveScreenshot("screenshot.png")) { qDebug() << "Screenshot saved to screenshot.png"; } return a.exec(); } ``` 该代码使用DXGI和D3D11 API创建设备和交换链,然后获取主适配器和后缓冲器纹理。然后,它创建一个CPU可读的纹理,将后缓冲器纹理复制到该纹理,然后将其映射到内存以获取图像数据。最后,它使用Qt的QImage类将图像数据保存文件。请注意,此代码需要使用Windows SDK和DirectX SDK来构建。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周星星的星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值