上周实验了opencv 的视频编解码功能,实验与调研结果表明opencv 对视频的编解码支持不够友好。于是决定使用ffmpeg 负责解码与编码工作,opencv 负责对解码后的数据进行处理。
本周在参考ffmpeg官方例程的基础上完成了对H265视频的解码,处理,再编码的工作。解码使用hevc-cuvid解码器,编码使用hevc-nvenc编码器,实验过程存在丢帧的现象,目前正在调试中,下面是具体的实验记录。
基于ffmpeg的HEVC编解码实验
GPU型号:GeForce GTX 1080
CUDA版本:CUDA 9.0
FFmpeg版本:ffmpeg-20180818-de1b44c-win64
IDE:Visual Studio 2015
ffmpeg不同编解码器的使用方法大体相同,使用GPU进行编解码时需要保证已安装好编解码器需要调用的底层环境。比如使用h264_qsv编解码器需要安装Intel Media SDK ,调用hevc-nvenc需要安装CUDA环境以及NVIDIA CODEC SDK,下面依次介绍ffmpeg的编解码过程。
解码流程
解码部分初始化部分相对简单,不需要设置很多复杂得参数,只需要完成相应数据的内存分配,并正确指定解码器的种类便可。
ffmepg按帧进行解码,我们的实验中每次读取4096 Byte送进解析器av_parser_parse2(),解析器会自动解析出一帧压缩的数据。配合使用avcodec_send_packet(dec_ctx, pkt)与avcodec_receive_frame(dec_ctx, frame) 来解压一帧数据,下图为解压流程。

解压完一帧数据便存储于ffmpeg自定义的数据结构 frame 中,我们可以将此帧数据转换为opencv中的 MAT 类型并调用opencv中的相应函数进行处理,再将处理完成之后的数据转为ffmepg的frame数据进行编码。
编码流程
相对于解码而言,编码需要设置更多的参数,更多的是对编码器的配置。且不同的编码器对应的配置存在一些差别。比如,使用hevc_nvenc 编码时 max_b_frames 参数必须设置为 0,否则会导致编码器加载失败

在调试过程中,设置 av_log_set_level(64); 可以得到很多重要的提示信息,非常有利于找到问题的原因。
编码器的其它参数设置如下:
- c->bit_rate = 400000;
- c->width = 1920;
- c->high = 1080;
- c->time_base = (AVRational) {1, 25};
- c->framerate = (AVRational) {25, 1};
- c->max_b_frames = 0;
- c->pix_fmt = AV_PIX_FMT_NV20LE ;
上面可以看出,我们设置的像素存储格式不是 AV_PIX_FMT_YUV420P ,那是因为调试的过程中发现使用hevc-cuvid后的数据格式为 AV_PIX_FMT_NV20LE ,这也是YUV420P的一种,只是在ffmpeg中数据的存储方法上有些变化。
编码流程总的来说于解码类似,使用avcodec_send_frame(enc_ctx, frame) 发送一帧数据到编码器,再配合使用avcodec_receive_packet(enc_ctx, pkt) 接收编码后的数据。
整体流程
整体流程即将上面的编解码流程结合,再加上对解码后得到的每帧图像进行处理。在完成对编解码的调试后,这个过程中最主要的便是对解码后数据的处理。现在主要的问题便是想整个过程都在GPU中进行处理,避免时间浪费在数据的转移过程中。目前了解到的信息是,opencv中的数据处理可以全程在gpu中进行,但ffmpeg到opencv数据类型的转换过程还没有深入了解。
