翻译自VPF原作者论坛:python硬解码过程及VPF安装总结
引言

本篇是对VPF原作者在论坛上所发布内容的翻译,内容是关于VPF的方法以及原理介绍,其基本上涵盖了整个python硬解码的过程,通过它能对视频的编解码有一个很全面的认识,关于VPF的安装总结的全过程如下链接:
Video Processing Framework框架的编译过程,Video Processing Framework框架的启动全过程
VideoProcessingFramework介绍
从Kepler一代的NVIDIA GPU起,就开始支持加速的硬件视频编码,自Fermi一代以来的所有GPU,都借助NVIDIA Video Codec SDK支持硬件视频加速解码。
它表现出出色的性能和灵活性,与此同时,它需要C / C ++的知识。另一个选择是使用第三方库和应用程序,比如FFmpeg或GStreamer,它们再次需要内置C / C ++专业知识,并且要为每个用户自定义。
但是,硬件加速视频功能或许对更广泛的受众有帮助,VPF即视频处理框架,其目的是在运用Python处理视频时借助NVIDIA GPU的简单且强大的工具,VPF利用NVIDIA Video Codec SDK来提升灵活性与性能,还为开发人员提供Python所固有的易用性。
VPF是一组C ++库以及Python绑定,它能为视频处理任务提供完整的硬件加速,这些任务包括解码、编码、代码转换,还有GPU加速的色彩空间和像素格式转换,VPF是基于Apache 2许可发布的,基于CMake的开源跨平台软件。它依靠FFmpeg库进行解复用,依靠pybind11项目来构建Python绑定。
VPF把C ++视频处理类导出至PyNvCodec这个Python模块里。为阐述易用性,让我们从一个快速的代码片段着手,该代码片段展示了怎样在GPU上实现完全硬件加速的视频转码,且无需在主机与设备之间复制原始帧:
import PyNvCodec as nvc
gpuID = 0
encFile = “大野兔1080p H.264格式的影片文件”(这里是根据原英文推测的中文含义,因为原英文看起来像是一个视频文件名,具体含义需结合更多背景信息确定) 。
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()
如果输入文件结束,解码器将返回零表面。
if not (rawSurface.GetCudaDevicePtr()):
break
encFrame = nvEnc.EncodeSingleSurface(rawSurface)
if(encFrame.size):
frameByteArray = bytearray(encFrame)
xcodeFile.write(frameByteArray)
编码器是异步的,所以我们需要刷新它。
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的核心部分包含PyNvDecoder类和PyNvEncoder类,这两个类是针对NVIDIA Video Codec SDK的Python绑定。VPF操作存在两种主要的数据类型:
GPU端内存对象的分配复杂,且会严重影响性能,所有归还Surface的情况,拥有它们的情况,以及Surface下次调用时重用先前返回的VPF类方法的情况,都存在。不同的是,VPF类方法每次被调用时都会返回新的NumPy数组实例。移动构造函数用于避免内存复制开销。
PyNvDecoder类支持仅NV12像素格式,PyNvEncoder类也支持仅NV12像素格式,这么做是为了简单起见。一组颜色空间和像素格式转换类支持其他像素格式。所有转换都是GPU加速的,且在VRAM内存中完成,目的是提高性能。
PyNvDecoder 该类有五个主要方法:
DecodeSingleSurface
从输入视频中解码单个帧,返回一个带有解码像素的Surface。下次用户调用这个方法时,之前返回的Surface有可能会被再次使用。要是没有解码帧,那么解码后的Surface的GetCudaDevicePtr方法将会返回零。
DecodeSingleFrame
从输入视频中解码出单帧,返回一个带有解码像素的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,Flush是一种将刷新编码器帧队列的方法。
以下是受支持的编码器参数的列表:
参数
类型
含义
profile
串
编码配置文件。
H264可能的值有,baseline,main,high 。
hevc的可能值:main。
lookahead
整数
前瞻的大小。
vbvinit
整数+单位
VBV初始延迟(以位为单位),可以以1,K,M为单位。
bitrate
整数+单位
平均比特率,可以以1,K,M为单位。
fps
整数
帧率。
preset
串
编码预设。
可能的值有:default,hp,hq,bd,ll,ll_hp,ll_hq,lossless,lossless_hp 。
constqp
整数
constqp速率控制模式的QP值。
qmin
整数
最小QP值。
qmax
整数
最大QP值。
cq
整数
VBR模式的目标恒定质量水平
可能的值:[0,51],0=auto。
initqp
整数
初始QP值。
temporalaq
(无值)启用临时AQ。
vbvbufsize
整数+单位
VBV缓冲区大小(以位为单位),可以1,K,M为单位。
bf
整数
连续B帧的数量。
rc
串
速率控制模式。
可能的值为:constqp,vbr,cbr,cbr_lowdelay_hq,cbr_hq,vbr_hq 。
aq
整数
启用空间AQ并设置其强度
可能的值:[0,15],0=auto。
maxbitrate
整数+单位
最大比特率,可以以1,K,M为单位。
gop
整数
GOP(图片组)的长度。
codec
串
视频编解码器。可能的值:h264,hevc。
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加速的色彩空间和像素格式转换,以下是受支持的转化列表:
PySurfaceConverter 有一种方法:
Execute
在GPU上进行转换操作,把句柄按照输出格式返回给Surface。当下次用户调用这个方法的时候,之前返回的Surface有可能会被再次使用。
总结
VPF为开发人员提供了一个简单且功能强大的Python工具,这个工具用于完全硬件加速的视频编码、解码以及处理类。因为有Python绑定下的C ++代码,所以能在数十行代码中实现较高的GPU利用率。解码后的视频帧以NumPy数组的形式公开,也以CUDA设备指针的形式公开,这样做是为了简化交互并扩展功能。VPF没有对NVIDIA Video Codec SDK设置任何约束,VPF准许您充分发挥NVIDIA专业级GPU的潜能。
原文链接: