近期有解码H265格式的1080P@60压缩视频需求,使用之前的ffmpeg软解方案发现解不过来了,超极本解码一帧耗时20ms左右,所以想研究下硬解方案。
1、首先想到的是ffmpeg的硬解方案,一直对ffmpeg的硬解有所了解但是因为之前软解满足需求,没有多大动力去研究,这次遇到瓶颈简单查了下,资源很多,ffmpeg在examples\hw_decoder.c就提供很好的示例程序,网上的资料大多基于这个demo的。
2、其他还有英伟达的cuda方案,百度,谷歌出来有人做,做的还不错,据说能达到700帧,但是cuda依赖太多,技术难度较大,等到以后做2K,4K的265解码时再研究。
最后解决方案确定为使用ffmpeg自带硬解方案,简单,有现成的官方demo,参考资料也比较多。
代码编写比较简单,参考hw_decoder.c自己稍作修改就能改成接受实时流解码的方式,不过其中遇到一些问题,在此记录:
1、经测试我的笔记本和台式机都只支持4种方式解码:cuda、qsv、d3d11、dxva2;
测试发现qsv不能使用,检测支持qsv解码器但是解码失败;
cuda解码很快但是因为解码后的yuv需要从GPU拷贝到CPU(也可以不用拷贝,可以直接用GPU做渲染,不过没研究明白),这拷贝耗时达到了15~20ms,相比软解没啥提升,论坛上有人说最新的ffmpeg解决了这个问题,没有验证,暂时放弃;
d3d这种方式解码加拷贝耗时相比软解有所提升,已经可以满足1080p60 265解码延时需求了,试图通过GPU解码不拷贝到CPU而是在GPU里直接渲染,感觉有点复杂,而且原有的显示方式是将cpu的yuv数据通过openGL渲染,改动有点费力。
dxva2方式效率和拷贝速度相比d3d这种方式进一步提升,测试在低压版的超极本上解码加拷贝耗时在10ms以内,基本满足需求,我查看了potplayer的硬解方式,发现他硬解也是用dxva2,这种方式对硬件要求小,最终选定这个方案。
需要注意的是(硬解码出来的YUV格式并不固定,做渲染时具体处理或者通过ffmpeg将解码出来的AVFrame转换为YUV420p格式的AVFrame);
size = av_image_get_buffer_size(tmp_frame->format, tmp_frame->width,
tmp_frame->height, 1);
buffer = av_malloc(size);
if (!buffer) {
fprintf(stderr, "Can not alloc buffer\n");
ret = AVERROR(ENOMEM);
goto fail;
}
ret = av_image_copy_to_buffer(buffer, size,
(const uint8_t * const *)tmp_frame->data,
(const int *)tmp_frame->linesize, tmp_frame->format,
tmp_frame->width, tmp_frame->height, 1);
官方demo就是最好的demo,一开始没想到从ffmpeg源码中找示例,百度了不少,各家有各家修改的理由,最后发现还是官方demo最简洁,干净,把官方demo解文件修改成解实时流很轻松。
等有时间研究研究在GPU里解码直接渲染的方式,感觉对以后2K,4K高帧解码会有奇效。
还需要做下硬解软解适配,有的客户电脑比较老可能不支持硬解,还需要适配下。
代码下载地址:ffmpeg+dxva2硬解-编解码文档类资源-CSDN下载