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 <io.h>       /* _setmode() */
#include <fcntl.h>    /* _O_BINARY */
#endif

#include <stdint.h>
#include <stdio.h>
#include <x264.h>

#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 <input.yuv >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( &param, "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( &param, "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( &param );
    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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容