一般来说,windows下的比较常用的编码为h264和h265(也叫hevc),用cpu编码的字符串为libx264和libx265,但是cpu编码特别消耗cpu而且帧率不高,特别是libx265,CPU编码帧率很低的。这是我们就需要用GPU进行编码,h264对应的3种常用的GPU编码字符串为h264_qsv,h264_cuvid,h264_amf;h265对应的3种常用的GPU编码字符串为hevc_qsv,hevc_cuvid,hevc_amf,分别对应于intel,英伟达和AMD的显卡。
要用到硬件GPU编码,常用步骤一般为:
1、检测GPU编码能力
2、设置参数,并打开编码器
3、编码encode
下面是具体代码:
1、检测GPU编码能力:
bool CFFFindEncoder::Find(char* szName)
{
AVCodecContext *c= NULL;
AVPacket *pkt;
AVCodec *codec=avcodec_find_encoder_by_name(szName);
if(codec==0)
return false;
c = avcodec_alloc_context3(codec);
if (!c)
{
return false;
}
//MessageBox(0,L"3",0,0);
pkt = av_packet_alloc();
if (!pkt)
{
return false;
}
c->bit_rate = 8000000;
c->width = 1920;
c->height = 1080;
c->time_base.num=1;
c->time_base.den=25;
c->framerate.num=25;
c->framerate.den=1;
c->gop_size = 10;
c->max_b_frames = 0;
const enum AVPixelFormat *pix_fmt;
for (pix_fmt = codec->pix_fmts; *pix_fmt != -1; pix_fmt++)
{
char format[200]={};
sprintf(format,"%s\n",av_get_pix_fmt_name(*pix_fmt));
}
c->pix_fmt = codec->pix_fmts[0];//AV_PIX_FMT_YUV420P;
int ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
return false;
}
avcodec_free_context(&c);
av_packet_free(&pkt);
return true;
}
如上面的代码所示,这段代码可以万无一失的检测是否有相应的编码能力。我们设置了基本的参数,如分辨率,码率,帧率,GOP等,c->pix_fmt = codec->pix_fmts[0];是设置的默认的编码器支持的颜色空间fmt,
const enum AVPixelFormat *pix_fmt;
for (pix_fmt = codec->pix_fmts; *pix_fmt != -1; pix_fmt++)
{
char format[200]={};
sprintf(format,"%s\n",av_get_pix_fmt_name(*pix_fmt));
}
这段代码则是枚举支持的所有fmt。一般libx264(libx265)支持的fmt最多,从多到少一般为x264>cuvid>qsv>=amf。
2、设置参数,并打开编码器:
重点讲讲2类参数:
1是设置CBR和CQP(CRF):通俗讲即码率模式和质量模式。
当然除了CBR还有VBR,ABR等。只是个人觉得这2种方式基本上就够了,码率模式适合网络传输,质量默认适合录像存储。
2是设置编码缓存数,网络传输时需要讲这个缓存设置为0,不然编码会有延迟,一般为1~8帧。对于高实时性的直播来说,这是不可接受的。
编码传输设置列表:
编码缓存数(低延迟设置) | 码率模式 | 质量模式 | |
libx264,libx265 | av_dict_set(&options, "tune", "zerolatency", 0); | pCodecCtx->bit_rate = m_nBPS; pCodecCtx->rc_min_rate =m_nBPS; pCodecCtx->rc_max_rate = m_nBPS; pCodecCtx->bit_rate_tolerance = m_nBPS; pCodecCtx->rc_buffer_size=m_nBPS; pCodecCtx->rc_initial_buffer_occupancy = pCodecCtx->rc_buffer_size*3/4; |
av_opt_set_int(pCodecCtx->priv_data, "crf", m_quality, 0); |
h264_qsv,hevc_qsv | av_opt_set(pCodecCtx->priv_data,"async_depth","1",0); | 同上 | pCodecCtx->flags=AV_CODEC_FLAG_QSCALE; pCodecCtx->global_quality=m_quality*FF_QP2LAMBDA; |
h264_cuvid,hevc_cuvid | av_opt_set(pCodecCtx->priv_data,"delay","0",0); | 同上 | pCodecCtx->flags=AV_CODEC_FLAG_QSCALE; |
h264_amf,hevc_amf | 无(缓存1帧) | 同上 | av_opt_set_int(pCodecCtx->priv_data,"qp_i",m_quality,0); av_opt_set_int(pCodecCtx->priv_data,"qp_p",m_quality,0); av_opt_set_int(pCodecCtx->priv_data,"qp_b",m_quality,0); |
需要说明的是:pCodecCtx的定义和获取:
AVCodecContext *pCodecCtx= NULL; AVCodec *codec=avcodec_find_encoder_by_name(szName); pCodecCtx = avcodec_alloc_context3(codec);
编码缓存数(低延迟设置):
对于低延迟的应用,编码缓存数是必不可少的。libx264,libx265的低延迟这个代码是随处可见的,av_dict_set(&options, "tune", "zerolatency", 0);但是其他基于GPU编码的低延迟设置,在网络居然找不到相关资料,没办法,只能自己看ffmpeg源码了,在此归纳总结下,希望能帮助到同样迷茫的你。
h264_qsv,hevc_qsv:av_opt_set(pCodecCtx->priv_data,"async_depth","1",0);
h264_cuvid,hevc_cuvid:av_opt_set(pCodecCtx->priv_data,"delay","0",0);
h264_amf,hevc_amf没找到相应设置,如果你找到了可以分享下。但是不太影响,因为只缓存了一帧。
CBR的设置:如果只设置了pCodecCtx->bit_rate = m_nBPS;这句设置了码率(个人理解是ABR平均码率),但是控制不了码率的上下波动。所以需要一起设置rc_min_rate和rc_max_rate。
QP和CRF的设置:
为什么libx264没有设置CQP而是设置的CRF,这个我也一知半解,按照推荐的来吧,知道原因的留言分享下。(引用:和CQP那种直接限定帧级QP的方式相比,CRF模式多了对每帧画面运动复杂度的考量,从而可以自适应的调节每帧QP的大小。所以比起CQP码控,CRF可以在实际产品中使用。)
m_quality的取值范围均为0~51,0为最好。
h264_qsv,hevc_qsv的m_quality为什么要乘以FF_QP2LAMBDA,这个就需要看ffmpeg的源码了。
h264_cuvid,hevc_cuvid没什么可以说的。
h264_amf,hevc_amf为什么是这3个,其实也是看的ffmpeg源码,如果设置了这3个,那么就是CQP模式。
以上内容都是平时日常经验总结,如有不妥,请轻点喷!
2023.9.24
于成都