一般来说,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帧。对于高实时性的直播来说,这是不可接受的。

编码传输设置列表:

需要说明的是: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   

于成都

09-28 14:03