能否直接从当前程序的GPU显存中提取原始或编码的摄像头视频帧?
过去一年我一直在从事Camera Video Frame Capturing相关开发,尝试基于webrtc及相关技术打造一款视频流插件,我了解目前市场上已有PixelStreaming等可用于视频流传输的插件。
近期我开展了新的研究,核心目标如下:
由于latency是开发更可靠的流传输插件的核心指标,我一直在尽力优化Camera Video Frame Capturing全流程的性能,同时尝试减少该过程中的额外数据跳转环节。
我现在的核心疑问是:是否可以直接从GPU中提取RAW或Encoded格式的摄像头视频帧?
我使用的是Nvidia GPU (GTX GEFORCE),目前可以通过Python模块调用Nvidia-SMI编程获取NVIDIA GPU的状态信息,但该方式无法让我获取、提取运行在GPU上的摄像头的视频帧。
简单示例
举个简单例子,摄像头采集的帧直接由GPU而非CPU处理,流程如下图:
我也参考过安卓端摄像头到CPU到GPU的最低开销方案相关资料。
至于Nvidia Encoder,我也有相关开发经验,已成功通过NvEnc实现原始视频帧的编码,也熟悉其原生C/C++实现方式。
再举个简单的例子,我已经可以通过CUDA C++编程调用cudaMalloc在GPU中分配内存,也可以通过cudaMemcpy将分配的GPU内存拷贝到主机程序中,示例代码如下:
int main() { const unsigned int N = 1048576; const unsigned int bytes = N * sizeof(int); int *h_a = (int*)malloc(bytes); int *d_a; cudaMalloc((int**)&d_a, bytes); memset(h_a, 0, bytes); cudaMemcpy(d_a, h_a, bytes, cudaMemcpyHostToDevice); cudaMemcpy(h_a, d_a, bytes, cudaMemcpyDeviceToHost); return 0; }
请问有没有相关的plugin、module、library或者实现建议,可以让当前运行的程序直接从GPU中提取视频帧,无需每次都从摄像头采集帧?
解决方案
可直接落地的实现路径
优先采用NVIDIA GPUDirect for Video(GDFV)方案
这是NVIDIA官方推出的专门针对低延迟视频采集的技术,支持UVC协议的USB摄像头、专业采集卡等设备直接将采集到的帧通过DMA方式写入GPU显存,完全绕开CPU内存中转。你的GTX系列GeForce显卡只要驱动版本在R470及以上均可支持,拿到的显存指针可直接被CUDA、NvEnc调用,无需额外拷贝,端到端延迟可压到10ms以内。Windows平台可走Media Foundation硬件采集管线
配置Media Foundation采集源时开启MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS参数,设置输出格式为D3D11纹理,采集到的帧会直接存储在D3D11显存中,再通过CUDA提供的cudaGraphicsD3D11RegisterResource接口将纹理注册为CUDA可访问资源,就能直接读取显存内的RAW帧数据,也可直接将D3D11纹理送入NvEnc编码,全程无CPU拷贝,实测比普通CPU采集方案延迟低2~5ms。Linux平台可走V4L2 + DMA-BUF + CUDA互操作路径
配置V4L2采集缓冲区为DMA-BUF模式,采集的帧会直接存入GPU可映射的缓冲区,再通过cudaImportExternalMemory接口将DMA-BUF导入为CUDA显存指针,即可直接访问,不需要经过CPU内存中转。
现成库/模块推荐
- C++开发可直接用FFmpeg的硬件加速分支,配置采集输入时指定
hwaccel cuda参数,FFmpeg会自动将采集到的帧导入CUDA显存,你可以在回调中直接拿到带CUDA显存指针的AVFrame,无需额外处理。 - Python开发可使用
PyCUDA搭配OpenCV的cudacodec模块,通过cudacodec.VideoCapture接口可直接将摄像头帧采集到CUDA显存,返回cuda.GpuMat对象,可直接用于后续处理,不需要从CPU侧Mat做GPU拷贝。
注意事项
- 消费级GeForce显卡的GPUDirect Video最多支持同时2路采集流,多路采集需求需要更换NVIDIA专业级显卡。
- 所用摄像头需要支持UVC 1.5及以上协议,老旧UVC 1.1摄像头不支持DMA直写显存,仍然会走CPU中转。
- 拿到GPU显存内的帧指针后,可直接传入NvEnc的
EncodeFrame接口编码,不需要拷回CPU,实现全流程GPU处理,最大程度降低延迟。
内容的提问来源于stack exchange,提问作者Muhammad Usman Bashir


