快手Y-tech
最新技术干货分享
摘要
在云端渲染场景中,服务端高性能硬件除了可以快速处理特效,也可以利用其实现快速编解码的操作,为云端特效处理提速。本文将介绍如何利用服务端GPU实现硬编硬解及一些特殊场景下的优化。
正文
提到音视频编解码,大家可能会第一时间想到ffmpeg,大家平时可能会常用ffmpeg命令行做一些音视频处理。ffmpeg是一个非常强大的跨平台的编解码库,在其编解码能力中,除了对一些软编软解能力的封装外,对不同平台不同硬件特性做了封装处理,比如MacOS及iOS上的VideoToolBox,Android平台的MediaCodec,Intel核显加速QSV,NVIDIA的NVCodec等,这里主要介绍在服务器端利用ffmpeg中对NVIDIA显卡加速的封装实现硬编硬解,因篇幅有限,这里会重点介绍如何针对渲染场景做些特殊处理,对于官方示例中已有的案例不会做详细介绍。
一、安装
首先从ffmpeg的编译安装开始讲起,因为需要使用nvcodec相关的能力,所以在ffmpeg时需要开启相关的功能进行编译。根据官方文档https://trac.ffmpeg.org/wiki/HWAccelIntro,只需要两步操作(在这之前需要保证机器上有NVIDIA显卡以及驱动及cuda已经正常安装)。
第一步,将nv-codec相关库下载编译。
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
cd nv-codec-headers
make
sudo make install
二、编解码
在官方示例中有三个示例可以了解编解码流程:
1. https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/hw_decode.c,实现了输入一个视频,同时可以指定编码器,输出yuv信息的功能。
2. https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/decode_video.c,实现了输入一个视频,输出yuv信息的功能。
在示例中,体现了如图1所示通用的解码流程,编码相反。下面对示例中的几个API做下解释。
1. av_hwdevice_ctx_create()
创建硬件设备相关的上下文信息AVHWDeviceContext和对硬件设备进行初始化。这一步在后续针对使用nvidia设备优化中会使用到。
2. av_hwdevice_find_type_by_name()
根据名称查找对应的AVHWDeviceType,支持的类型如源码所示https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/hwcontext.h#L27,这里其实就是指定使用和硬件特性相关的编解码方式。
3. avcodec_find_encoder_by_name()
4. avcodec_find_decoder_by_name()
查找ffmpeg支持的解码器,可通过 ffmpeg -decoders 查看。
5. 在官方编码示例中:https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_video.c#L124。
if (codec->id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
在云端渲染场景中,最终还是需要我们将解码帧转为OpenGL相关的纹理进行相关的操作。按照图5流程,需要把解码后的帧数据拷贝到CPU中,然后再进行格式的转换,通过示例可以看到数据在GPU和CPU中左右横跳,所以针对这种情况,我们需要做进一步优化。
通过对相关API的查询,我们了解到,通过硬解直接拿到的AVFrame中存放的数据是从解码器中直接拿到的数据指针,比如使用NVIDIA相关平台h264_cuvid拿到的是cuda memory,MacOS平台使用videotoolbox拿到的是数据指针CVPixelBufferRef,这些数据结构都提供了平台相关API来构造OpengGL纹理,或者Mac下可以转为Metel texture,如此便可改为图6流程。
以下代码为AVFrame转为OpenGL纹理的过程,因为ffmpeg的API中存在大量void类型指针,而且文档会标明所有可能的类型,比如:https://ffmpeg.org/doxygen/3.3/structAVBufferRef.html#acb8452e99cd75074b93800b532c6ea4b。
所以一些情况下通过源码可以了解到ffmpeg与平台类型API是如何交互的,以及不同数据类型是如何交互,https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/cuviddec.c#L461。编码则同上面流程相反。
// cudaContext可以从设置的VideoCodecContext->hw_device_ctx中拿到
cuCtxPushCurrent(cudaContext);
// 映射OpenGL memory和Cuda Memory
cuGraphicsGLRegisterImage(&(cuTexRes[channel]), texYuv[channel], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
cuGraphicsMapResources(1, &(cuTexRes[channel]), 0);
CUarray mapped_array;
cuGraphicsSubResourceGetMappedArray(&mapped_array, cuTexRes[channel], 0, 0);
// 内存拷贝
CUDA_MEMCPY2D cpy = {
.srcMemoryType = CU_MEMORYTYPE_DEVICE,
.srcDevice = (CUdeviceptr)((AVFrame *)frame->data[channel]),
.srcPitch = width,
.dstMemoryType = CU_MEMORYTYPE_ARRAY,
.dstArray = mapped_array,
.WidthInBytes = width,
.Height = height,
};
cuMemcpy2D(&cpy);
// 释放相关资源,gl纹理可正常使用
cuGraphicsUnmapResources(1, &(cuTexRes[channel]), 0);
cuCtxPopCurrent(&cudaContext);
通过上面的示例,我们利用了NVIDIA提供的硬件加速能力来提升编解码速度,但是对于多任务场景下还有一些需要注意的地方。
1. 对于编码,NVIDIA做了明确的限制,桌面级显卡大多限制了同时3路编码并发,而对于服务器级别显卡则没有限制。https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new。
启用方式如下:
#获取当前的GPU编号,一般服务器物理机上可能有多张显卡,所以需要确认当前实例使用的GPU的编号,默认是0,保险起见通过命令获取
DEVICE_NUM=`nvidia-smi --query-gpu=index --format=csv | tail -n 1`
export CUDA_VISIBLE_DEVICES=$DEVICE_NUM
#运行mps server进程
nvidia-cuda-mps-control -d
总结
通过对ffmpeg与硬件相关交互的了解,我们可以针对不同使用场景做更加精细的优化,除了特效处理场景下对OpenGL相关的交互,还可以继续探索譬如机器学习场景下cuda相关数据的交互,使得编解码耗时不再成为云端特效运行效率的瓶颈。
参考文献
[1]ffmpeg: https://github.com/FFmpeg/FFmpeg/
[2]HWAccelIntro: https://trac.ffmpeg.org/wiki/HWAccelIntro
[3]Video Encode and Decode GPU Support Matrix: https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new
[4]mps:https://docs.nvidia.com/deploy/mps/index.html
[5]libx264 vs nvenc: https://developer.nvidia.com/zh-cn/blog/turing-h264-video-encoding-speed-and-quality/
你可能还想看
等你来
Y-tech
快手Y-tech部门致力于通过计算机视觉、计算机图形学、深度学习、AR/VR/HCI等多领域的交叉,帮助每个人更好地表达、创作以及互动。Y-tech在北京、深圳、Palo Alto均有研发团队。如果你对我们做的事情感兴趣,欢迎联系并加入我们!
Y-tech长期招聘(全职或实习生):计算机视觉、计算机图形学、UE/Unity开发、技术美术、AI推理引擎、AI工程架构、美颜/特效技术、平台/工具开发、技术产品经理等方向的优秀人才。联系方式:ytechservice@kuaishou.com
Y-tech///