VPF:适用于 Python 的开源视频处理框架,加速视频任务、提高 GPU 利用率
- 2020 年 2 月 11 日
- 筆記
同时,由于 Python 绑定下的 C ++代码,它使开发者可以在数十行代码中实现较高的 GPU 利用率。解码后的视频帧以 NumPy 数组或 CUDA 设备指针的形式公开,以简化交互过程及其扩展功能。
目前,VPF 并未对 NVIDIA Video Codec SDK 附加任何限制,开发者可充分利用 NVIDIA 专业级 GPU 的功能。
Python 中的硬件加速视频处理框架 VPF
VPF 是基于 CMake 的开源跨平台框架,它依赖于 FFmpeg 库来进行(de)muxing 和 pybind11 项目从而构建 Python 绑定。它包含了一组开源的 C ++库和 Python 绑定,可与其封闭源代码 Codec SDK 进行交互。
该框架的主要功能是简化从 Python 开发 GPU 加速视频编码/解码的过程,可为视频处理任务(例如解码,编码,代码转换以及 GPU 加速的色彩空间和像素格式转换)提供完整的硬件加速。
尽管 Python 不是性能最高的语言,但它易于使用;在 NVIDIA 发布此视频处理框架之后,它相当于在现有 Video Codec SDK C ++ 堆栈周围的 Python wrapper,将用于在 Kepler 及更高版本上基于 GPU 的视频编码/解码。这使得 VPF 在利用基于 GPU 的高性能视频加速的同时,也获得了易于阅读/编写的代码。
NVIDIA Video Codec SDK 使用效果示意图
同时值得注意的是,VPF 还利用 NVIDIA Video Codec SDK(一套全面的 API,包括用于 Windows 和 Linux 上硬件加速视频编码和解码的高性能工具,示例和文档)来提高灵活性和性能,并为开发人员提供 Python 固有的易用性。目前,该代码在 GitHub 上已开源。
Github 地址: https://github.com/NVIDIA/VideoProcessingFramework
代码示例及结果
在官网博客宣布 VPF 时,开发者也提供了一个简短的 Python 代码示例,该示例使用 PyNvCodec 模块显示 Python 中的视频转码:
import PyNvCodec as nvc gpuID = 0 encFile = "big_buck_bunny_1080p_h264.mov" xcodeFile = open("big_buck_bunny_1080p.h264", "wb") nvDec = nvc.PyNvDecoder(encFile, gpuID) nvEnc = nvc.PyNvEncoder({'preset': 'hq', 'codec': 'h264', 's': '1920x1080'}, gpuID) while True: rawSurface = nvDec.DecodeSingleSurface() # Decoder will return zero surface if input file is over; if not (rawSurface.GetCudaDevicePtr()): break encFrame = nvEnc.EncodeSingleSurface(rawSurface) if(encFrame.size): frameByteArray = bytearray(encFrame) xcodeFile.write(frameByteArray) # Encoder is asynchronous, so we need to flush it encFrames = nvEnc.Flush() for encFrame in encFrames: encByteArray = bytearray(encFrame) xcodeFile.write(encByteArray)
尽管这一示例的设计简单,但 VPF 仍具有良好的性能。上面显示的代码转换示例足以使 RTX 5000 GPU 上的 Nvenc 单元饱和,如下所示:
Big Buck Bunny 序列包含 14315 帧,可以在 32 秒内进行转码,而无需使用任何先进的技术(例如生产者-消费者模式),解码器和编码器将在单独的线程中启动共享解码器队列,从而可以在约 447fps 的速度下进行转码。由于所有转码均在 GPU 上完成,因此没有明显的 CPU 负载。
VPF 使用类说明
VPF 中包含了多个类,其核心部分是 PyNvDecoder 和 PyNvEncoder 类,它们是与 NVIDIA Video Codec SDK 的 Python 绑定。
PyNvDecoder 和 PyNvEncoder 类支持 NV12 像素格式,所有转换均通过 GPU 加速,并在 VRAM 内存中完成,以提高性能。其中——
PyNvDecoder 类有五个主要方法:
- DecodeSingleSurface 从输入视频解码单帧,返回带有解码像素的 Surface。下次用户调用此方法时,先前返回的 Surface 可能会被重用。如果未解码帧,则解码后的 Surface 的 GetCudaDevicePtr 方法将返回零;
- DecodeSingleFram 从输入视频解码单帧,返回带有解码像素的 NumPy 数组。下次用户调用此方法时,将返回另一个 NumPy 数组实例。如果未解码帧,它将返回空的 NumPy 数组。此操作将设备复制到主机内存;
- Width 返回解码的帧宽度;
- Height 返回解码的帧高度;
- PixelFormat 返回解码的帧像素格式。
用户使用 DecodeSingleSurface 和 DecodeSingleFrame 时,不会破坏解码器的内部状态。解码器类支持 H.264 和 H.265 编解码器。
PyNvEncoder 类有六个方法:
- EncodeSingleSurface 以原始像素获取 NV12 Surface,对其进行编码,然后将基本视频比特流作为 NumPy 数组返回。编码器是异步的,因此此方法可能会在前几次调用时返回空数组(取决于编码器设置),这不是编码错误;
- EncodeSingleFrame 以原始像素获取 NumPy 数组,对其进行编码,然后将基本视频比特流作为 NumPy 数组返回。编码器是异步的,因此此方法可能在前几次调用时返回空数组(取决于编码器设置);
- Flush 冲洗编码器。除非编码器队列中的所有原始帧都已编码,否则它不会返回,并返回带有基本流字节的 NumPy 数组的列表;
- Width 返回编码的帧宽度;
- Height 返回编码的帧高度;
- PixelFormat 返回编码的帧像素格式。
如果用户使用 EncodeSingleSurface 和 EncodeSingleFrame,则不会破坏编码器的内部状态。此外,PyNvEncoder 可以获取任意分辨率的输入帧,并在实际编码之前即时在 GPU 上调整其大小。
编码器类支持 H.264 和 H.265 编解码器,并且具有较低的延迟,因此在编码会话结束时,应调用 Flush 刷新编码器帧队列。
HardwareSurface 类包含一个包装器 CUdeviceptr:
- GetCudaDevicePtr 将 CUdeviceptr 返回到 CUDA 内存对象。
对于主机和设备之间的内存传输,有两个名为 PyFrameUploader 和 PySurfaceDownloader 的类:
- PyFrameUploader 用于将 NumPy 数组上传到 GPU;
- UploadSingleFrame 将一个 numpy 数组上传到 GPU,再将句柄返回到上传的 Surface。下次用户调用此方法时,先前返回的 Surface 可能会被重用。
PySurfaceDownloader 类用于从 GPU 下载 Surface,它只包含一种方法:
- DownloadSingleSurface 将 GPU 端 Surface 下载到 CPU 端 numpy 数组中。下次用户调用此方法时,将返回另一个 numpy 数组实例。
PySurfaceConverter 类用于 GPU 加速的色彩空间和像素格式转换。以下是受支持的转化列表:
- YUV420 至 NV12
- NV12 到 YUV420
- NV12 转 RGB
PySurfaceConverter 类包含一种方法:
- Execute 在 GPU 上执行转换,将句柄以输出格式返回给 Surface。下次用户调用此方法时,先前返回的 Surface 可能会被重用。
而 VPF 运行的主要数据类型有两种:
- 用于 CPU 端数据的 NumPy 数组;
- 用户透明 Surface 类,表示 GPU 端数据;
由于 GPU 端内存对象分配很复杂,并且会严重影响性能,因此所有归还 Surface,并在下次调用时重用先前返回的 VPF 类方法。
与此不同的是,VPF 类方法每次被调用时都会返回新的 NumPy 数组实例。移动构造函数可避免内存复制的运行成本。
其它开源视频处理框架
一、RxFFmpeg
RxFFmpeg 是基于 ( FFmpeg 4.0 + X264 + mp3lame + fdk-aac ) 编译的适用于 Android 平台的音视频编辑、视频剪辑的快速处理框架。
包含:视频拼接,转码,压缩,裁剪,片头片尾,分离音视频,变速,添加静态贴纸和 gif 动态贴纸,添加字幕,添加滤镜,添加背景音乐,加速减速视频,倒放音视频,音频裁剪,变声,混音,图片合成视频,视频解码图片等主流特色功能。
RxFFmpeg 开源地址: https://github.com/microshow/RxFFmpeg
二、VidGear
VidGear 是一个围绕 OpenCV 视频 I/O 模块的轻量级 python 包装器,它使用多线程 Gears(又名 API)构建,每个都有独特的开拓性功能。
这些 API 提供了易于使用,高度可扩展的多线程包装器,这些包装器围绕着许多底层的最新 python 库,例如 OpenCV,FFmpeg,picamera,pafy,pyzmq 和 python-mss ➶,可以在各种设备和平台上实现高速视频帧读取功能 。它也是 imutils 库视频模块的重新实现,修复了所有主要错误,并附带了直接网络流支持。
VidGear 开源地址: https://pypi.org/project/vidgear/
VPF 博客地址: https://devblogs.nvidia.com/vpf-hardware-accelerated-video-processing-framework-in-python/