播放器开发之ffmpeg 硬件解码方案
硬件编解码的概念
硬件编解码是⾮CPU通过烧写运⾏视频加速功能对⾼清视频流进⾏编解码,其中⾮CPU可包括GPU、FPGA或者 ASIC等独⽴硬件模块,把CPU⾼使⽤率的视频解码⼯作从CPU⾥分离出来,降低CPU的使⽤负荷,使得平台能 ⾼效且流畅的执⾏视频的编解码动作。
软件编解码的概念
软件编解码指的是在信息传输的过程中,视频等信号直接通过CPU进⾏编解码。如此⽽来,会增加CPU的⼯作 负荷,提升功耗,如果CPU能⼒不⾜,则软件也会受到⼀定程度的影响。 由于CPU拥有更强⼤的计算能⼒以及更精确的数据位宽,因此CPU能更好地兼容各种编解码的⼯作。
总结一下硬件编解码和软件编解码的优缺点:
硬件编解码的优点:
-
可以分离出音视频编解码的工作,少量占用CPU资源
-
编解码性能更高,同等条件下速度更快
-
同等条件下,硬件编解码的功耗更低
硬件编解码的缺点:
-
软件支持度不如软件编解码,起步较晚
-
由于硬件功能性限制,兼容性较低
-
在画面输出补偿、画质增强等技术方面有一定缺陷
-
需要独立进行硬件设置,对小白用户不太友好
软件编解码的优点:
-
编解码器更易于开发,协议兼容度更高
-
拥有更多画面输出补偿和画质增强技术,画质更优
软件编解码的缺点:
-
对CPU性能要求较高,在高帧率游戏或高清视频播放时CPU负担较重
GPU编解码的常⽤技术⽅案
对应gpu编解码编程,硬件⼚家都有相应SDK⽅案,应⽤开发者可以直接调⽤⼚家的SDK 来完成编解码器⼯作。
FFmpeg⽅案
Windows的d 3 d 11 va 屏蔽不同的gpu。
ffmpeg对⼚家/或平台SDK进⾏封装和集成,实现部分的硬件编解码。主要关注nvida和Intel的⽅案。
许多硬件解码器的⼀个共同特点是能够在适合其他组件使⽤的硬件表⾯中⽣成输出(对于独⽴显卡,这意味着表 ⾯在卡上的内存中⽽不是在系统内存中)—— 这通常对播放很有⽤,因为在呈现输出之前不需要进⼀步复制,并 且在某些情况下,它还可以与⽀持硬件表⾯输⼊的编码器⼀起使⽤,以避免在转码情况下进⾏任何复制 (Darren注:从gpu拷⻉数据到内存,和内存拷⻉数据gpu都⽐较耗时)。
硬件编码器⽣成的输出质量通常⽐好的软件编码器(如 x 264 )低,但通常速度更快并且不使⽤太多 CPU 资源。
使用ffmpeg查看⽀持的可⽤硬件加速
ffmpeg -hwaccels
代码使用
注册阶段
1通过 av_hwdevice_find_type_by_name() 根据名称自动寻找硬件解码 API 如 "qsv"、"cuvid"、"d3d11va"、"dxva2"等
2使用 avcodec_get_hw_config 找到对应的硬件解码格式回调 cuda -> cuvid 解码器 ->h264_nvenc
3将解码器格式赋值为硬件解码格式
4创建硬件设备上下文 av_hwdevice_ctx_create
使用阶段
5进行硬件解码 avcodec_send_packet avcodec_receive_frame 6转换硬件解码的数据 av_hwframe_transfer_data() 从 GPU 内存转移到 CPU 内存中
下图所示(选取参考文献):
硬解init
if(m_isHw_device && stream_index == AVMEDIA_TYPE_VIDEO)
{
// 初始化硬件解码器(在avcodec_open2前调用)
if(initHWDecoder(avctx,codec) < 0){
msg_queue_->notify_msg(FFP_MSG_VIDEO_HW_DECODE_NONE);
}
}
if ((ret = avcodec_open2(avctx, codec, NULL)) < 0) {
goto fail;
}
int FFPlayer::initHWDecoder(AVCodecContext*avctx, const AVCodec *codec)
{
if(!avctx && !codec) return -2;
enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hw_device_type.data()); // 根据名称自动寻找硬解码
if (type == AV_HWDEVICE_TYPE_NONE){
return -1;
}
//2.获取编码器支持的硬件配置:
for(int i = 0; ; i++)
{
const AVCodecHWConfig* config = avcodec_get_hw_config(codec, i); // 检索编解码器支持的硬件配置。
if(!config)
{
LOG(ERROR)<< "打开硬件解码器失败!";
m_isHw_device = false;
hw_device_type.clear();
return -1; // 没有找到支持的硬件配置
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX
&& config->device_type == type) // 判断是否是设备类型
{
hw_pix_fmt = config->pix_fmt;
LOG(INFO) << "打开硬件解码器:" << av_hwdevice_get_type_name(config->device_type);
// 打开指定类型的设备,并为其创建AVHWDeviceContext。
int ret = av_hwdevice_ctx_create(&hw_device_ctx, config->device_type, nullptr, nullptr, 0);
if(ret < 0)
{
fprintf(stderr, "Failed to create specified HW device.\n");
return ret;
}
avctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); // 创建一个对AVBuffer的新引用。
avctx->get_format = get_hw_format; // 由一些解码器调用,以选择将用于输出帧的像素格式
return 1;
}
}
return -1;
}
硬解后数据获取
从gpu拿解码数据
//硬解2 从硬件buffer拷贝的内存来
//这里可以考虑直接使用frame给显存显示, 这里如果开启耗时增加%75左右
if(is->m_isHw_device){
hw_frame = av_frame_alloc();
if(hw_decoder_gpu_tocpu_copy(hw_frame,frame) < 0)
tmp_frame = frame;
else
tmp_frame = hw_frame;
}
else{
tmp_frame = frame;
}
int Decoder::hw_decoder_gpu_tocpu_copy(AVFrame *dst, AVFrame *src)
{
if (src->format == is->hw_pix_fmt){
av_hwframe_map在ffmpeg3.3以后才有,经过测试av_hwframe_transfer_data的耗时大概是av_hwframe_map的【1.5倍】
int ret = av_hwframe_transfer_data(dst, src, 0); // 映射硬件数据帧
// av_hwframe_map()
if(ret < 0)
{
LOG(ERROR)<<"av_hwframe_map failt " << is->hw_pix_fmt;
return -1;
}
} else{
dst = src;
}
return 1;
}
总结
把解码当成解压缩文件,压缩算法越高级,得到的文件内存占用量就少。但是cpu使用率就高,硬件解码也是同一道理。
硬件编解码器与软件编解码器的对比:
-
输出质量:
-
硬件编解码器的输出质量通常低于优质的软件编解码器(如x264)。
-
为了达到相同的感知质量,硬件编解码器需要更高的码率。
-
在相同码率下,软件编解码器的输出质量通常更好。
-
-
性能和效率:
-
硬件编解码器的编解码速度更快,同时也更节省CPU资源。
-
这使其更适合实时视频编解码的应用场景。
-
-
功能支持:
-
硬件编解码器往往只支持编解码器的部分功能配置,而不是全部。比如本机有qsv硬件,但是没有qsv对应的专属解码器程序,导致解码失败。
-
例如在H.264编码中,可能只支持8位4:2:0格式,而不支持更复杂的配置。
-
-
硬件表面输出:
-
硬件编解码器可以直接在显卡显存中生成输出表面。
-
这避免了从GPU到系统内存的数据拷贝,提高了效率。
-
还可以与支持硬件表面的编码器配合使用,消除转码过程中的数据拷贝。
-
-
API支持:
-
不同的硬件编解码器可能支持不同的API标准,如VAAPI、NVIDIA NVDEC/NVENC等。
-
FFmpeg等工具可以提供对这些API的访问能力。
-
综上所述,硬件编解码器在性能和效率方面有优势,但在输出质量和功能支持上可能略逊于优秀的软件编解码器。选择时需要权衡具体的应用需求。
测试
测试4k影片,正常播放30s,本次测试保留了从gpu到cpu拷贝到缓存队列,如果直接将gpu转给显存,性能将大幅提升。
不使用硬解后 cpu使用率波动15-25左右
使用硬解后 cpu使用率 波动 7-18左右
参考文献
Qt-FFmpeg开发-视频播放【硬解码】(2) - mahuifa - 博客园
学习资料分享
0voice · GitHub