H264编码系列之ffmpeg和x264码率控制分析

概念

h264编码算法复杂、参数众多,单码率控制又分为三种模式。

  • VBR(Variable Bit Rate)即动态比特率,其码率可以随着图像的复杂程度的不同而变化,因此其编码效率比较高,Motion发生时,马赛克很少。码率控制算法根据图像内容确定使用的比特率,图像内容比较简单则分配较少的码率(似乎码字更合适),图像内容复杂则分配较多的码字,这样既保证了质量,又兼顾带宽限制。这种算法优先考虑图像质量。
  • CBR(Constant Bit Rate)是以恒定比特率方式进行编码,有Motion发生时,由于码率恒定,只能通过增大QP来减少码字大小,图像质量变差,当场景静止时,图像质量又变好,因此图像质量不稳定。这种算法优先考虑码率(带宽)。
  • CVBR(Constrained VariableBit Rate)它是VBR的一种改进方法。这种方法的兼顾了以上两种方法的优点:在图像内容静止时,节省带宽,有Motion发生时,利用前期节省的带宽来尽可能的提高图像质量,达到同时兼顾带宽和图像质量的目的。同时i_bitrate也需要设置。

码率控制

注:符号“∝”表示成正比例

间接影响

关键帧间隔

x264 ffmpeg 说明
命令行 字段 命令行 字段
max-keyint i_keyint_max - g gop_size 关键帧的最大间隔帧数
min-keyint i_keyint_min keyint_min 关键帧的最小间隔帧数

码率∝关键帧间隔(视频质量其他参数恒定)

分辨率

x264 ffmpeg 说明
命令行 字段 命令行 字段
resize(滤镜) i_width -s width
i_height height

码率∝分辨率(视频质量其他参数恒定)

帧率

x264 ffmpeg 说明
命令行 字段 命令行 字段
fps i_fps_num fps time_base.num 频率分子/时间基分子
i_fps_den time_base.den 频率分母/时间基分母

码率∝帧率(视频质量其他参数恒定)
注:

x264: i_fps_num = 15; i_fps_den= 1;//帧率15。
ffmpeg:time_base.num=1;time_den=1;//帧率15。

B帧数

x264 ffmpeg 说明
命令行 字段 命令行 字段
b-adapt b_bframe_adaptiv b_frame_strategy 设定弹性B帧配置决策算法。此设定控制x264如何决定要放置P帧或B帧。
0 关闭:永远选择B帧。此值效果相当于旧选项no-b-adapt。
1 “快速”算法:较快。b-frames设定越高,增速效果越明显。此模式下强烈推荐配合使用--bframes 16。
2 “优化”算法:较慢。b-frames设定越高,减速效果越明显。
bframes i_bframe max_b_frames 设置x264采用的最大连续B帧数。假如没有B帧,x264数据流会是这样:IPPPPP...PI。若 --bframes 2,则最多2个连续P帧可以被替换为B帧,比如: IBPBBPBPPPB...PI。

码率∝1/b帧数(视频质量其他参数恒定)

直接影响

x264 ffmpeg 说明
字段 取值 字段 取值
x264_param_t->rc x264中控制码率的结构体
i_rc_method X264_RC_CQP 0 恒定质量(动态码率)
X264_RC_CRF 1 恒定码率
X264_RC_ABR 2 平均码率
码率控制方式
i_qp_constant 恒定qp值,0代表无损压缩 cqp 取值范围0~51。0表示无损压缩 固定量化因子
i_qp_min
i_qp_max
qp_min最小qp取值,默认10;
qp_max最大qp取值,默认51
qmin
qmax
取值范围0-51,通常在20~40之间
i_qp_step max_qdiff 最大的在帧与帧之间进行切变的量化因子的变化量。
i_bitrate bit_rate 码率
i_vbv_max_bitrate rc_max_rate 最大瞬时码率
f_rf_constant_max 是实际质量最大值
f_rf_constant 默认23 是实际质量,越大图像越花,越小越清晰

未选择时,优先选择的顺序是 bitrate > QP > CRF,会按照该顺序排查参数,直到发现某种类型参数合法时确定类型。

if( bitrate )
    rc_method = ABR;
else if ( qp || qp_constant )
    rc_method = CQP;
else
    rc_method = CRF;`

x264参数解析

缺省参数和预设参数请分别参考x264_param_default和x264_param_default_preset。

typedef structx264_param_t
{
    /* CPU 标志位 */
    unsigned int cpu;
    int i_threads; /* 并行编码多帧 */
    int b_deterministic; /*是否允许非确定性时线程优化*/
    int i_sync_lookahead; /* 线程超前缓冲 */

    /* 视频属性 */
    int i_width; /* 宽度*/
    int i_height; /* 高度*/
    int i_csp; /* 编码比特流的CSP,仅支持i420,色彩空间设置 */
    int i_level_idc; /* level值的设置*/
    int i_frame_total; /* 编码帧的总数, 默认 0 */
    /*Vui参数集视频可用性信息视频标准化选项 */
    struct
    {
        /* they will be reduced to be 0 < x <= 65535 and prime */
        int i_sar_height;
        int i_sar_width; /* 设置长宽比 */

        int i_overscan;/* 0=undef, 1=no overscan, 2=overscan 过扫描线,默认"undef"(不设置),可选项:show(观看)/crop(去除)*/

        /*见以下的值h264附件E */
        int i_vidformat;/* 视频格式,默认"undef",component/pal/ntsc/secam/mac/undef*/
        int b_fullrange; /*Specify full range samples setting,默认"off",可选项:off/on*/
        int i_colorprim; /*原始色度格式,默认"undef",可选项:undef/bt709/bt470m/bt470bg,smpte170m/smpte240m/film*/
        int i_transfer; /*转换方式,默认"undef",可选项:undef/bt709/bt470m/bt470bg/linear,log100/log316/smpte170m/smpte240m*/
        int i_colmatrix; /*色度矩阵设置,默认"undef",undef/bt709/fcc/bt470bg,smpte170m/smpte240m/GBR/YCgCo*/
        int i_chroma_loc; /* both top & bottom色度样本指定,范围0~5,默认0 */
    } vui;

    int i_fps_num;
    int i_fps_den;
    /*这两个参数是由fps帧率确定的,赋值的过程见下:
    { float fps;
    if( sscanf( value, "%d/%d", &p->i_fps_num,&p->i_fps_den ) == 2 )
      ;
      else if( sscanf( value, "%f", &fps ) )
      {
      p->i_fps_num = (int)(fps * 1000 + .5);
      p->i_fps_den = 1000;
      }
      else
      b_error = 1;
      }
    Value的值就是fps。*/

    /*流参数 */
    int i_frame_reference; /* 参考帧最大数目 */
    int i_keyint_max; /* 在此间隔设置IDR关键帧 */
    int i_keyint_min; /* 场景切换少于次值编码位I, 而不是 IDR. */
    int i_scenecut_threshold; /*如何积极地插入额外的I帧 */
    int i_bframe; /*两个相关图像间P帧的数目 */
    int i_bframe_adaptive; /*自适应B帧判定*/
    int i_bframe_bias; /*控制插入B帧判定,范围-100~+100,越高越容易插入B帧,默认0*/
    int b_bframe_pyramid; /*允许部分B为参考帧 */
    /*去块滤波器需要的参数*/
    int b_deblocking_filter;
    int i_deblocking_filter_alphac0; /* [-6, 6] -6 light filter, 6 strong */
    int i_deblocking_filter_beta; /* [-6, 6] idem */
    /*熵编码 */
    int b_cabac;
    int i_cabac_init_idc;

    intb_interlaced; /* 隔行扫描 */
    /*量化 */
    int i_cqm_preset; /*自定义量化矩阵(CQM),初始化量化模式为flat*/
    char *psz_cqm_file; /* JM format读取JM格式的外部量化矩阵文件,自动忽略其他—cqm 选项*/
    uint8_t cqm_4iy[16]; /* used only if i_cqm_preset == X264_CQM_CUSTOM */
    uint8_t cqm_4ic[16];
    uint8_t cqm_4py[16];
    uint8_t cqm_4pc[16];
    uint8_t cqm_8iy[64];
    uint8_t cqm_8py[64];

    /* 日志 */
    void (*pf_log)( void *, int i_level, const char *psz, va_list );
    void *p_log_private;
    int i_log_level;
    int b_visualize;
    char *psz_dump_yuv; /* 重建帧的名字 */

    /* 编码分析参数*/
    struct
    {
        unsigned int intra; /* 帧间分区*/
        unsigned int inter; /* 帧内分区 */

        int b_transform_8x8; /* 帧间分区*/
        int b_weighted_bipred; /*为b帧隐式加权 */
        int i_direct_mv_pred; /*时间空间队运动预测 */
        int i_chroma_qp_offset; /*色度量化步长偏移量 */

        int i_me_method; /* 运动估计算法(X264_ME_*) */
        int i_me_range; /* 整像素运动估计搜索范围 (frompredicted mv) */
        int i_mv_range; /* 运动矢量最大长度(inpixels). -1 = auto, based on level */
        int i_mv_range_thread; /* 线程之间的最小空间. -1 = auto, based on number of threads. */
        int i_subpel_refine; /* 亚像素运动估计质量 */
        int b_chroma_me; /* 亚像素色度运动估计和P帧的模式选择 */
        int b_mixed_references; /*允许每个宏块的分区在P帧有它自己的参考号*/
        int i_trellis; /* Trellis量化,对每个8x8的块寻找合适的量化值,需要CABAC,默认0 0:关闭1:只在最后编码时使用2:一直使用*/
        int b_fast_pskip; /*快速P帧跳过检测*/
        int b_dct_decimate; /* 在P-frames转换参数域 */
        int i_noise_reduction; /*自适应伪盲区 */
        float f_psy_rd; /* Psy RD strength */
        float f_psy_trellis; /* Psy trellis strength */
        int b_psy; /* Toggle all psy optimizations */

        /*,亮度量化中使用的无效区大小*/
        int i_luma_deadzone[2]; /* {帧间, 帧内} */

        int b_psnr; /* 计算和打印PSNR信息 */
        int b_ssim; /*计算和打印SSIM信息*/
    } analyse;

    /* 码率控制参数 */
    struct
    {
        int i_rc_method; /* X264_RC_* */

        int i_qp_constant; /* 0-51 */
        int i_qp_min; /*允许的最小量化值 */
        int i_qp_max; /*允许的最大量化值*/
        int i_qp_step; /*帧间最大量化步长 */

        int i_bitrate;/*设置平均码率 */
        float f_rf_constant; /* 1pass VBR, nominal QP */
        float f_rate_tolerance;
        int i_vbv_max_bitrate; /*平均码率模式下,最大瞬时码率,默认0(与-B设置相同) */
        int i_vbv_buffer_size; /*码率控制缓冲区的大小,单位kbit,默认0 */
        float f_vbv_buffer_init; /* <=1: fraction of buffer_size. >1: kbit码率控制缓冲区数据保留的最大数据量与缓冲区大小之比,范围0~1.0,默认0.9*/
        float f_ip_factor;
        float f_pb_factor;

        int i_aq_mode;/* psy adaptive QP. (X264_AQ_*) */
        float f_aq_strength;
        int b_mb_tree; /* Macroblock-tree ratecontrol. */
        int i_lookahead;

        /* 2pass 多次压缩码率控制 */
        int b_stat_write; /* Enable stat writing in psz_stat_out */
        char *psz_stat_out;
        int b_stat_read; /* Read stat from psz_stat_in and use it */
        char *psz_stat_in;

        /* 2pass params(same as ffmpeg ones) */
        float f_qcompress; /* 0.0 => cbr, 1.0 => constant qp */
        float f_qblur; /*时间上模糊量化 */
        float f_complexity_blur; /* 时间上模糊复杂性 */
        x264_zone_t *zones; /* 码率控制覆盖 */
        int i_zones; /* number of zone_t's */
        char *psz_zones; /*指定区的另一种方法*/
    } rc;

    /* Muxing parameters*/
    int b_aud; /*生成访问单元分隔符*/
    int b_repeat_headers; /* 在每个关键帧前放置SPS/PPS*/
    int i_sps_id; /* SPS 和 PPS id 号 */

    /*切片(像条)参数 */
    int i_slice_max_size; /* 每片字节的最大数,包括预计的NAL开销. */
    int i_slice_max_mbs; /* 每片宏块的最大数,重写i_slice_count */
    int i_slice_count; /* 每帧的像条数目: 设置矩形像条. */

    /* Optionalcallback for freeing this x264_param_t when it is done being used.
  * Only used when the x264_param_t sits in memory for an indefiniteperiod of time,
  * i.e. when an x264_param_t is passed to x264_t in an x264_picture_t orin zones.
  * Not used when x264_encoder_reconfig is called directly. */
    void (*param_free)( void* );
} x264_param_t;

x264官方提供的范例

重要的点:

  • x264_encoder_delayed_frames 返回当前延迟(缓冲)帧的数目,在要结束编码时使用。
  • x264_picture_alloc和x264_picture_clean需要配对使用,否则有内存泄漏。
#ifdef _WIN32
#include        /* _setmode() */
#include     /* _O_BINARY */
#endif

#include 
#include 
#include 

#define FAIL_IF_ERROR( cond, ... )\
do\
{\
    if( cond )\
    {\
        fprintf( stderr, __VA_ARGS__ );\
        goto fail;\
    }\
} while( 0 )

int main( int argc, char **argv )
{
    int width, height;
    x264_param_t param;
    x264_picture_t pic;
    x264_picture_t pic_out;
    x264_t *h;
    int i_frame = 0;
    int i_frame_size;
    x264_nal_t *nal;
    int i_nal;

#ifdef _WIN32
    _setmode( _fileno( stdin ),  _O_BINARY );
    _setmode( _fileno( stdout ), _O_BINARY );
    _setmode( _fileno( stderr ), _O_BINARY );
#endif

    FAIL_IF_ERROR( !(argc > 1), "Example usage: example 352x288 output.h264\n" );
    FAIL_IF_ERROR( 2 != sscanf( argv[1], "%dx%d", &width, &height ), "resolution not specified or incorrect\n" );

    /* Get default params for preset/tuning */
    if( x264_param_default_preset( ¶m, "medium", NULL ) < 0 )
        goto fail;

    /* Configure non-default params */
    param.i_bitdepth = 8;
    param.i_csp = X264_CSP_I420;
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0;
    param.b_repeat_headers = 1;
    param.b_annexb = 1;

    /* Apply profile restrictions. */
    if( x264_param_apply_profile( ¶m, "high" ) < 0 )
        goto fail;

    if( x264_picture_alloc( &pic, param.i_csp, param.i_width, param.i_height ) < 0 )
        goto fail;
#undef fail
#define fail fail2

    h = x264_encoder_open( ¶m );
    if( !h )
        goto fail;
#undef fail
#define fail fail3

    int luma_size = width * height;
    int chroma_size = luma_size / 4;
    /* Encode frames */
    for( ;; i_frame++ )
    {
        /* Read input frame */
        if( fread( pic.img.plane[0], 1, luma_size, stdin ) != luma_size )
            break;
        if( fread( pic.img.plane[1], 1, chroma_size, stdin ) != chroma_size )
            break;
        if( fread( pic.img.plane[2], 1, chroma_size, stdin ) != chroma_size )
            break;

        pic.i_pts = i_frame;
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )
                goto fail;
        }
    }
    /* Flush delayed frames */
    while( x264_encoder_delayed_frames( h ) )
    {
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )
                goto fail;
        }
    }

    x264_encoder_close( h );
    x264_picture_clean( &pic );
    return 0;

#undef fail
fail3:
    x264_encoder_close( h );
fail2:
    x264_picture_clean( &pic );
fail:
    return -1;
}

你可能感兴趣的:(H264编码系列之ffmpeg和x264码率控制分析)