x264编码详细文字全过程 (1) x264_param_default( x264_param_t *param ) 作用: 对编码器进行参数设定 cqm:量化表相关信息 csp: 量化表相关信息里的memset( param->cqm_4iy, 16, 16 ); memset( param->cqm_4ic, 16, 16 ); memset( param->cqm_4py, 16, 16 ); memset( param->cqm_4pc, 16, 16 ); memset( param->cqm_8iy, 16, 64 ); memset( param->cqm_8py, 16, 64 ); (2)static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) 初始化 1. getopt_long(nargc, nargv, options, long_options, idx) 得到入口地址的向量与方式的选则 2. getopt_internal(nargc, nargv, options) 解析入口地址向量 (3) static int Encode( x264_param_t *param, cli_opt_t *opt ) /* Create a copy of param */ h->param=param /* VUI */vui信息主要包括帧率、图像尺寸等信息 /* Init x264_t */ x264_sps_init( h->sps, 0, &h->param );序列图像集 x264_pps_init( h->pps, 0, &h->param, h->sps);图像参数集 /* Init frames. */ 初始化并开辟帧空间 /* init mb cache */ 对前一宏块的信息保存,因为是初始化,所以作为第一个宏块的参考,后面会有x264_macroblock_cache_load( h, i_mb_x, i_mb_y );它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值 /* init cabac adaptive model */ /* init CPU functions */ 初始化cpu对各种分块的参数设定 /* rate control */ 1. x264_t *x264_encoder_open ( x264_param_t *param ) 这个函数是对不正确的参数进行修改,并对各结构体参数和cabac编码,预测等需要的参数进行初始化 2、p_read_frame( &pic, opt->hin, i_frame + opt->i_seek, param->i_width, param->i_height )
读取一帧,并把这帧设为prev
3. i_file += Encode_frame( h, opt->hout, &pic );进入核心码层
核心编码层的总流程图:(x264.c)
1. x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out )对帧进行编码
2. i_size = x264_nal_encode( data, &i_data, 1, &nal[i] ) 网络打包编码
3. i_file += p_write_nalu( hout, data, i_size ) 把网络包写入到输出文件中去
4. 返回,对下一帧进行编码 下面一页是详细的流程图: 一.帧内详细流程图:
(1). x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out )对帧进行编码
1. /* 1: Copy the picture to a frame and move it to a buffer */
x264_frame_t*fenc=x264_frame_get( h->frames.unused );
x264_frame_copy_picture( h, fenc, pic_in );
fenc->i_frame = h->frames.i_input++;
x264_frame_put( h->frames.next, fenc );
x264_frame_init_lowres( h->param.cpu, fenc );//里面包含低象素的扩展,很多for循环,应该是抽头计算和半精度象素的扩展,要认真看
(2). 264_slicetype_decide( h );对slice类型的判定,里面也要看一下
(3). while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) ) bframes++; x264_frame_put(h->frames.current,x264_frame_get( &h->frames.next[bframes] ) );这主要是因为B帧必须等后面的非B帧编码结束后才能编码,所以把暂时不编的一系列B帧存入队列中,一直到非B帧才取出进行编码,之后再进行前面的B帧编码 do_encode: (4). 建立list0 & list1.我感觉 x264_reference_build_list( h, h->fdec->i_poc, i_slice_type ); 比特率控制初始化 x264_ratecontrol_start(h, i_slice_type, h->fenc->i_qpplus1 ); (5). 创建slice的头部数据 x264_slice_init( h, i_nal_type, i_slice_type, i_global_qp ); (6) i_frame_size = x264_slices_write( h );这是编码的关键了 1. x264_slice_header_write(&h->out.bs,&h->sh,h->i_nal_ref_idc ); /* Slice header */ 2. 一些初始化工作 3. for(mb_xy=h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; mb_xy++ )对一个slice中每个宏块进行循环遍历编码,其中const int i_mb_y = mb_xy / h->sps->i_mb_width;和const int i_mb_x = mb_xy % h->sps->i_mb_width;是对宏块位置在slice中的x,y坐标的定位,这个for语句几乎覆盖了整个x264_slices_write()函数 4. x264_macroblock_cache_load( h, i_mb_x, i_mb_y ); 它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值! 5. *****x264_macroblock_analyse( h );重点。通过一系列的SAD算出最优化方案,例如把I帧16×16的宏块分成16个4×4分别计算SAD和与原16×16SAD比较我感觉,在下面一层再详细分析。 a. x264_mb_analyse_intra( h, &analysis, COST_MAX );我感觉是在一个16×16的SAD,4个8×8的SAD和,16个4×4SAD和中选出最优方式进行,可能我的理解不对,里面的x264_mb_encode_i4x4( h, idx, a->i_qp );i8×8几个函数的跟踪有问题,跟得我都找不到,要仔细看(现在又能跟到了)
这边好像如果是直流分量在这里就进行量化ZIGZAG扫描了,不用等到x264_macroblock_encode( h )再完成了
b. x264_analyse_update_cache( h, &analysis ); 有对色度块的模式选择的计算,好像也有更新信息以为下次的预测作为参考
6. x264_macroblock_encode( h );
a. 判断宏块的类型
b. 根据判断的类型进行DCT,量化,ZIGZAG,并记录当前的模式为下次编码宏块(亚宏块)做参考
ZIGZAG的实现不明白(原来ZIGZAG有宏定义,在上面,现在明白了),反量化和IDCT的过程跟不进去,应该是汇编了!函数如下:( I 4×4 中 x264_mb_encode_i4x4( h, i, i_qp );)
x264_mb_dequant_4x4( dct4x4, h->dequant4_mf[CQM_4IY], i_qscale );
h->dctf.add4x4_idct( p_dst, i_stride, dct4x4 );
还有,这个函数跟踪不进去,应该是重构图像的反变换吧
h->dctf.add4x4_idct( p_dst, i_stride, dct4x4 );
h->mb.cache.intra4x4_pred_mode[x264_scan8[i]]=x264_mb_pred_mode4x4_fix(i_mode);这个值到底是怎么根据前面的模式改变的,可能是上面两个函数没能更进去所以模糊
c. 对色度块进行编码,QP限制在0-51之间,选定预测模式(DC的话值全为128)
x264_mb_encode_8x8_chroma( h, !IS_INTRA( h->mb.i_type ), i_qp );里面对两个色度信号分别编码,与亮度信号类似
d. 求亮度和色度的cbp,完全不明白是怎么求的,需要解决!现在有点明白,每个比特代表子块是不是全为0,但还没有全部明白,色度块cbp中0x02表示有AC,DC 0x01表示只有DC,
e.利用CBP判断要不要SKIP.,里面还关系到向量预测,明天好好看一下。 其中
h->mb.qp[h->mb.i_mb_xy] = h->mb.i_last_qp;这个为读下一个 qp的保存,不然解码端是读不出下一个 qp的,
关于CBP的理解还存在问题,他的8位比特各个代表的意思还不是十分明确,反正是对DC,AC的编码的选择。185页有介绍(新一代视频压缩标准毕厚杰)
7. 选用CABAC还是CAVLC
CABAC的原理实现没仔细看
8. x264_macroblock_cache_save( h );保存以为下次的预测作为参考
9. 一些收尾工作,为下次宏块作准备(看的比较粗) x264基于经验和感觉的码率控制策略 收藏 前提:
1 high-complexity or high-motion scenes,细节将不会很明显,此时高qp也是浪费
2 where motion compensation works well,在景物边沿的失真,只需在一帧中去掉,以后就都不会有.在这里投入有限的bits可以获得最好的图像质量性价比
3 已经编码一frame,可以预测其他qp下所需bit数.预测距离越远越不精确
4随着frame重要性降低,他们只配用更大的qp,i ,p ,参考b ,disposable b.依次降低
5H.264支持1frame内不同mb使用不同qp,x264不支持,而由rc返回统一qp。但有那个功能函数存在那个函数仅精确到每一行mb变一次qp
所以rc策略如下:
2pass:
step1 1pass编码,由qp推断某qp下framesize *0.6符合目标frame size的限制,得到这个qp
step2 修改qp 以满足requested total size(total是指整个Gop的大小,分段先编一边再一边)
step3 encode根据实际大小值修正预测的qp,并额外增加short-term compensation,针对开始和结束部分没有很多bits余地的位置.
1pass: abr (average bit rate )
step1 用半尺寸快速运动估计和SATD residuals 替换1st pass中相关部分,获得预测
step2 用之前的样本估计scale
step3 Overflow compensation 和2pass相似 限制filesize 牺牲图像质量
1pass,:constant bitrate (VBV compliant)!!!
VBV是指: Video Buffer Verifier
The Video Buffer Verifier (VBV) is a model hypothetical decoder buffer that will not overflow or underflow when fed a conforming MPEG bit stream. 包含2个因素.size和造成的delay
step1 same as abr
step2 Scaling factor is based on a local average (dependent on VBV buffer size) instead of all past frames
step3 stricter Overflow compensation , additional term to hard limit the QPs if the VBV is near empty. no hard limit is done for a full VBV这里更加严格的空限制,防止没有bits可以送出,破坏了cbr的传输
1pass, constant ratefactor: Constant Rate Factor (or Constant Quality) (1) Same as ABR. (2) The scaling factor is a constant based on the --crf argument. (3) No overflow compensation is done.
ratefactor是指:
constant quantizer: QPs are simply based on frame type.
RC中的蛋鸡悖论:为了计算当前帧中宏块的 RDO,需利用已定qp确定当前帧或宏块的cost预测每个宏块的mode mv ref等.
ratecontrol是在确定mode mv ref后决定qp,在此之前qp不能获得。
于是rdo与rc不知道先做哪个了. x264命令行参数解释 收藏 本文对应的是x264命令行模式,VFW方式也用相同的参数,不过是图形界面,可以自己找对应的英文。 使用格式:x264 默认选项 -o 输出文件 输入文件 [长x宽] 输入支持格式:RAW/y4m/avi/avs(编译时可选) 输出支持格式:264/mkv/mp4(编译时可选)
x264的许多参数可以有-/--两种输入法,笔者也不知道为什么。以下等价参数用“参 数1/参数2 <必需数值格式>”表示,参数尾部()内为个人推荐。
-h/--help 帮助
帧类型选项:
-I/--keyint <整数> 最大IDR帧间距,默认250 -i/--min-keyint <整数> 最小IDR帧间距,默认25 --scenecut <整数> 画面动态变化限,当超出此值时插入I帧,默认40 -b/--bframes <整数> 在IP帧之间可插入的B帧数量最大值,范围0~16,默认0 --no-b-adapt 关闭自适应B帧判定(-b设为1时可用,其他不推荐) --b-bias <整数> 控制插入B帧判定,范围-100~+100,越高越容易插入B帧,默认0 --b-pyramid 允许B帧做参考帧 --no-cabac 关闭内容自适应二进制算术编码(CABAC,高效率的熵编码)(会 提高速度,但严重影响质量) -r/--ref <整数> 最大参考帧数,范围0~16,默认1 --nf 关闭环路滤波(一种除马赛克算法) -f/--filter <alpha:beta>设置环路滤波的AlphaC和Beta的参数,范围-6-6,默认都为0
码率控制选项:
-q/--qp <整数> 固定量化模式并设置使用的量化值,范围0~51,0为无损压缩,默认26
-B/--bitrate <整数> 设置平均码率 --crf <整数> 质量模式,量化值动态可变(目前不太成熟,质量不如设置固定量化值) --qpmin <整数> 设置最小量化值,范围0~51,默认10 --qpmax <整数> 设置最大量化值,范围0~51,默认51 --qpstep <整数> 设置相邻帧之间的量化值差,范围0~50,默认4 --ratetol <小数> 平均码率模式下,瞬时码率可以偏离的倍数,范围0.1~100.0,默认1. 0 --vbv-maxrate <整数> 平均码率模式下,最大瞬时码率,默认0(与-B设置相同) --vbv-bufsize <整数> 码率控制缓冲区的大小,单位kbit,默认0 --vbv-init <小数> 码率控制缓冲区数据保留的最大数据量与缓冲区大小之比,范围0~1. 0,默认0.9
--ipratio <小数> I帧和P帧之间的量化系数,默认1.40 --pbratio <小数> P帧和B帧之间的量化系数,默认1.30 --色度-qp-offset <整数> 色度和亮度之间的量化差,范围-12~+12,默认0
-p/--pass <1|2|3> 多次压缩码率控制 1:第一次压缩,创建统计文件 2:按建立的统计文件压缩并输出,不覆盖统计文件, 3:按建立的统计文件压缩,优化统计文件 --stats <字符串> 统计文件的名称,默认"x264_2pass.log" --rceq <字符串> 速率控制公式,默认"blurCplx^(1-qComp)" --qcomp <小数> 线性量化控制,0.0为固定码率,1.0为固定量化值,默认0.6,只用于2- pass和质量模式 --cplxblur <小数> 根据相邻帧平滑量化值比例的最大值,范围0~99.9,默认20.0,只用 于2-pass和质量模式 --qblur <小数> 对统计文件结果平滑量化值比例的最大值,范围0~99.9,默认0.5,只用于 2-pass
--zones <z0>/<z1>/… 分段量化,格式为:<开始帧>,<结束帧>,<选项>,可选项为:q=< 整数>(量化值)或b=<小数>(码率倍数)
分析选项:
-A/--analyse <字符串> 动态块划分方法,默认"p8x8,b8x8,i8x8,i4x4"。可选项:p8x8/ p4x4/b8x8/i8x8/i4x4;none/all(p4x4需要p8x8. i8x8需要--8x8dct) --direct <字符串> 动态预测方式,默认"spatial"。可选项:none/spatial/temp oral/auto -w/--weightb 允许B帧加权预测(可以减少相邻B帧质量低的影响) --me <字符串> 对全像素块动态预测搜索的方式,默认"hex",可选项: dia:菱形搜索,半径1 (快) hex:正六边形搜索,半径2 umh:可变半径六边形搜索 esa:全面搜索(很慢,而且效果与umh几乎相同) --merange <整数> --me为umh/esa时的搜索半径,最大64,默认16 -m/--subme <整数> 动态预测和分区方式,可选项1~7,默认5(与压缩质量和时间关系密切 ,1是7速度的四倍以上) 1:用全像素块进行动态搜索,对每个块再用快速模式进行四分之一像素块精确搜索 2:用半像素块进行动态搜索,对每个块再用快速模式进行四分之一像素块精确搜索 3:用半像素块进行动态搜索,对每个块再用质量模式进行四分之一像素块精确搜索 4:用快速模式进行四分之一像素块精确搜索 5:用质量模式进行四分之一像素块精确搜索 6:进行I、P帧像素块的速率失真最优化(rdo) 7:进行I、P帧运动矢量及块内部的速率失真最优化(质量最好) --b-rdo B帧也进行rdo,需要--subme在6以上 --mixed-refs 可以在一帧内使用不同参考帧 --no-chroma-me 不进行色度的动态预测 --bime 可以平均B帧参考块的运动矢量 -8/--8x8dct 可以使用8x8的离散余弦变换(DCT) -t/--trellis <整数> Trellis量化,对每个8x8的块寻找合适的量化值,需要CABAC, 默认0 0:关闭 1:只在最后编码时使用 2:一直使用 --no-fast-pskip 关闭快速P帧跳过检测 --no-dct-decimate 关闭P帧联合编码(可以增加细节,但也会增大体积) --nr <整数> 噪声去除,范围0~100000,默认0
--cqm <字符串> 设置外部量化矩阵格式,默认"flat",可选项:jvt/flat --cqmfile <字符串> 读取JM格式的外部量化矩阵文件,自动忽略其他--cqm*选项 --cqm4 <list> 设置4x4的量化矩阵,用逗号分开,范围1~255的16个整数 --cqm8 <list> 设置8x8的量化矩阵,用逗号分开,范围1~255的64个整数 --cqm4i/--cqm4p/--cqm8i/--cqm8p 设置I、P帧不同的量化矩阵 --cqm4iy/--cqm4ic/--cqm4py/--cqm4pc 设置亮度、色度不同的量化矩阵
视频标准化选项: 这些选项与编码无关,不过如果要用mp4之类的播放器,可以设置,风险自担
--sar width:height 设置长宽比 --overscan <字符串> 过扫描线,默认"undef"(不设置),可选项:show(观看)/crop(去除 ) --videoformat <字符串> 视频格式,默认"undef",可选项:component/pal/ntsc/secam /mac/undef --fullrange <字符串> Specify full range samples setting,默认"off",可选项: off/on(我也不明白这是干什么的,请高手指点) --colorprim <字符串> 原始色度格式,默认"undef",可选项:undef/bt709/bt470m/ bt470bg,smpte170m/smpte240m/film --transfer <字符串> 转换方式,默认"undef",可选项:undef/bt709/bt470m/bt47 0bg/linear,log100/log316/smpte170m/smpte240m --colormatrix <字符串> 色度矩阵设置,默认"undef",undef/bt709/fcc/bt470bg,smpt e170m/smpte240m/GBR/YCgCo --chromaloc <整数> 色度样本指定,范围0~5,默认0
输入、输出选项:
--level <字符串> 设定等级(as defined by Annex A)(不明白,请高手指点) --fps <小数> 设定帧率 --seek <整数> 设定起始帧 --frames <整数> 最大编码帧数 -o/--output 指定输出文件
--threads <整数> 编码线程(使用分片技术) --thread-input 在编码线程中运行Avisynth --no-asm 关闭全部CPU优化指令 --no-psnr 关闭PSNR计算 --quiet 安静模式 -v/--verbose 显示每一个帧的信息 --progress 显示编码进程 --visualize 显示运动矢量 --sps-id <整数> 设置SPS和PPS的ID值,默认0 --aud 使用数据单元定义符号 x264中重要结构体说明 收藏 首先解释一下cli_opt_t的这个_t代表结构图可能是type的意思。同时还有很多i_ b_等作为前缀的变量,其中的I_表示int类型的变量 b表示bool类型的。依次类推。
正式进入主题。
typedef struct { int b_progress; int i_seek; hnd_t hin; hnd_t hout; FILE *qpfile; } cli_opt_t; 此结构体是记录一些与编码关系较小的设置信息的opt=option。结构体内部的变量都可以通过读取main()的参数获得。也就是argv。 b_progress表示一个bool类型的变量,看参数帮助 也就是x264--help你会知道,他是用来控制是否显示编码进度的一个东西。取值为0,1. I_seek 整数类型 表示开始从哪一帧编码。因为不一定从这个文件的第一帧开始编码,这是可以控制的。 Hnd_t(hnd=handle)是一个空指针, void *在C语言里空指针是有几个特性的,他是一个一般化指针,可以指向任何一种类型,但却不能解引用,需要解引用的时候,需要进行强制转换。采用空指针的策略,应该是为了声明变量的简便和统一。 Hin 指向输入yuv文件的指针。 Hout 指向编码过后生成的文件的指针。 Qpfile 是一个指向文件类型的指针,他是文本文件,其每一行的格式是framenum frametype QP 用于强制指定某些帧或者全部帧的帧类型和QP(quant param量化参数)的值。 x264_param_default( ¶m );
这部分设置编码参数的缺省值 附结构体param中部分变量的意义: param->i_csp = X264_CSP_I420; // 设置输入的视频采样的格式 param->vui.i_sar_width = 0; //VUI:video usability information param->i_fps_num = 10; //帧率 param->i_fps_den = 1; //用两个整型的数的比值,来表示帧率 /* Encoder parameters */ param->i_frame_reference = 1; //参考帧的最大帧数。 param->i_bframe = 0; //两个参考帧之间的B帧数目。 param->b_deblocking_filter = 1; //去块效应相关 param->b_cabac = 0; //cabac的开关 param->i_cabac_init_idc = -1; param->rc.b_cbr = 1; //constant bitrate 恒定码率控制模式 param->rc.i_bitrate = 0; //默认的码率 param->rc.i_rc_buffer_size = 0; //buffer的大小 param->rc.i_rc_init_buffer = 0; // param->rc.i_rc_sens = 100; ///* rate control sensitivity param->rc.i_rc_method = X264_RC_NONE; //码率控制,CQP(恒定质量)、//CRF(恒定码率)、ABR(平均码率) param->rc.i_qp_constant = 26; //qp的初始值,最大最小的qp值, param->rc.i_qp_min = 10; //最小的qp值 param->rc.i_qp_max = 51; //最大的qp值 param->rc.i_qp_step = 4; //qp[步长step。 param->rc.f_ip_factor = 1.4; //ip--i帧p帧的qp的差值 param->rc.f_pb_factor = 1.3; //pb--p帧b帧的qp的差值 /* Log */ //整个param的一个log文件 /*analyse */
param->analyse.intra = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8; //桢内分析
param->analyse.inter = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8 |X264_ANALYSE_PSUB16x16 | X264_ANALYSE_BSUB16x16; //桢间分析
param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL; //预测模式 param->analyse.i_me_method = X264_ME_HEX; //运动估计模式 param->analyse.i_me_range = 16; //运动估计范围 param->analyse.i_subpel_refine = 5; param->analyse.b_chroma_me = 1;
param->analyse.i_mv_range_thread = -1;
param->analyse.i_mv_range = -1; // set from level_idc
param->analyse.i_direct_8x8_inference = -1; // set from level_idc
param->analyse.i_chroma_qp_offset = 0;
param->analyse.b_fast_pskip = 1;
param->analyse.b_dct_decimate = 1;
param->analyse.i_luma_deadzone[0] = 21;
param->analyse.i_luma_deadzone[1] = 11;
param->analyse.b_psnr = 1;
param->analyse.b_ssim = 1;
param->i_cqm_preset = X264_CQM_FLAT; //自定义量化矩阵(CQM),初始化量化模式为flat
typedef struct
{
/* In: force picture type (if not auto)
* If x264 encoding parameters are violated in the forcing of picture types,
* x264 will correct the input picture type and log a warning.
* The quality of frametype decisions may suffer if a great deal of fine-grained
* mixing of auto and forced frametypes is done.
* Out: type of the picture encoded */
int i_type;
/* In: force quantizer for > 0 */
int i_qpplus1;
/* In: user pts, Out: pts of encoded picture (user)*/
int64_t i_pts;
/* In: raw data */
x264_image_t img;
} x264_picture_t;
具体的含义理解参考了read_frame_yuv()x264_picture_alloc();
I_type 指明被编码图像的类型,有X264_TYPE_AUTO X264_TYPE_IDR X264_TYPE_I X264_TYPE_P X264_TYPE_BREF X264_TYPE_B可供选择,初始化为AUTO,说明由x264在编码过程中自行控制。
I_qpplus1 :此参数减1代表当前画面的量化参数值。
I_pts :program time stamp 程序时间戳,指示这幅画面编码的时间戳。
Img :存放真正一副图像的原始数据。
typedef struct
{
int i_csp;
int i_plane;
int i_stride[4];
uint8_t *plane[4];
} x264_image_t;
Csp: color space parameter 色彩空间参数 X264只支持I420
i_Plane 代表色彩空间的个数。一般为3,YUV,初始化为
x264常用options整理 收藏
x264源码解析 2009年11月12日 星期四 22:44
1、x264学习笔记(9)--x264中16x16运动搜索过程 函数实现是函数 static void x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *a ) 1、大循环是参考帧的循环,从最近的一个参考帧开始搜索,一直到最远的一个参考帧;
2、x264学习笔记(10)---分像素的运动估计总结 得到分像素的值函数是下面两个函数,对照着 (1) static uint8_t *get_ref( uint8_t *src[4], int i_src_stride, uint8_t *dst, int * i_dst_stride, int mvx,int mvy, int i_width, int i_height ) { int qpel_idx = ((mvy&3)<<2) + (mvx&3); //取出运动矢量的分像素部分。 int offset = (mvy>>2)*i_src_stride + (mvx>>2); //偏移到所选的整像素点 uint8_t *src1 = src[hpel_ref0[qpel_idx]] + offset + ((mvy&3) == 3) * i_src_stride; /*src1和src2都分别指向的是1/2像素块,关键是这个hpel_ref0[qpel_idx]和hpel_ref1[qpel_idx],下面将详细介绍。 注意一点就是参考帧定义了uint8_t *p_fref[2][32][4+2]; /* last: lN, lH, lV, lHV, cU, cV */ 这里面的 4+2 的这个2代表色度,而这个4分别代表整像素,在整像素水平右边的1/2像素,在整像素垂直下面的1/2像素和整像素右下角的1/2像素。1/2像素的值已经在前面函数里面插值存好了,只要调用就可以了,而如果要进行1/4像素估计,要临时插值。现在这个函数 get_ref 中,src[0]、src[1]、src[2]、src[3]这传进来的就是分别是 lN, lH, lV, lHV */ if( qpel_idx & 5 ) /* qpel interpolation needed */ { uint8_t *src2 = src[hpel_ref1[qpel_idx]] + offset + ((mvx&3) == 3); pixel_avg( dst, *i_dst_stride, src1, i_src_stride, src2, i_src_stride, i_width, i_height );//1/4搜索时需要临时插值函数 return dst; } else { *i_dst_stride = i_src_stride; return src1; } } 按照 毕厚杰 的《新一代视频压缩编码标准——H.264/AVC》关于运动矢量那一节的介绍。看图6.22 那四个像素点,G为整像素点 b、h、i分别是lH, lV, lHV,也就是水平,垂直和对角线的值。 G b h i 对应为 src[0] src[1] src[2] src[3] 现在看这两个数组 static const int hpel_ref0[16] = {0,1,1,1,0,1,1,1,2,3,3,3,0,1,1,1}; static const int hpel_ref1[16] = {0,0,0,0,2,2,3,2,2,2,3,2,2,2,3,2}; 也按像素的平面图画出来的话 src[hpel_ref0[qpel_idx]]为 0 1 1 1 0 1 1 1 2 3 3 3 0 1 1 1 src[hpel_ref1[qpel_idx]]为 0 0 0 0 2 2 3 2 2 2 3 2 2 2 3 2 这上面的数字 0、1、2、3分别代表 整像素、水平1/2像素值、垂直1/2像素值 和对角线1/2像素值,也就是毕厚杰书中的 G、b、h、I 。这里要注意src[hpel_ref0[qpel_idx]]最后一行的 0 1 1 1 和src[hpel_ref1[qpel_idx]]最右边一列0 2 2 2不是当前的整像素0的1/2像素,而分别是其下面和右边一个整像素的对应的1/2像素值,因为 ((mvy&3) == 3) * i_src_stride 和((mvx&3) == 3)。 为什么要这么来排,是因为要根据1/4像素是通过1/2像素线性插值的公式来的,具体看下面这个函数。 (2) static inline void pixel_avg( uint8_t *dst, int i_dst_stride, uint8_t *src1, int i_src1_stride, uint8_t *src2, int i_src2_stride, int i_width, int i_height ) { //1/4搜索时需要临时插值函数 int x, y; for( y = 0; y < i_height; y++ ) { for( x = 0; x < i_width; x++ ) { dst[x] = ( src1[x] + src2[x] + 1 ) >> 1; //利用相邻半像素和两个像素取平均插值 } dst += i_dst_stride; src1 += i_src1_stride; src2 += i_src2_stride; } } 不过最后我有个疑问,那就是1/4插值后,应该原来的1/2 值保持不变的.但是分析发现,这个 b 、h、 i 这三个1/2像素中,h和i是不变的,不过 b会发生变化. 个人觉得 static const int hpel_ref1[16] = {0,0,0,0,2,2,3,2,2,2,3,2,2,2,3,2};如果改为 static const int hpel_ref1[16] = {0,0,1,0,2,2,3,2,2,2,3,2,2,2,3,2};则 b也不会发生变化. 所以这里打个问号?
3、x264学习笔记(11)---关于运动矢量MV不传输的问题 昨天看到H.264乐园群里面有人在讨论运动矢量MV不用传输的问题,就去看了下x264源代码,作个总结
编码端: 运动估计搜索得到的运动矢量MV是不需要传送的,需要传送的是MVD,MVD即运动矢量MV(运动估计得到)和运动矢量的预测矢量MVP(预测得到)的差值。 MVD = MV - MVP
解码端:通过预测得到MVP,将传输过来的MVD和MVP相加得到 MV = MVD + MVP,然后用这个MV去参考帧中获取预测象素值,最后把这个预测值和残差加一起, 作为重构像素值
x264中把这个过程放在了熵编码阶段,在这个函数里 x264_macroblock_write_cabac
MVD并保存下来以备传输的函数如下: static inline void x264_cabac_mb_mvd( x264_t *h, x264_cabac_t *cb, int i_list, int idx, int width, int height ) { int mvp[2]; int mdx, mdy;
/* Calculate mvd */ x264_mb_predict_mv( h, i_list, idx, width, mvp ); //预测MVP mdx = h->mb.cache.mv[i_list][x264_scan8[idx]][0] - mvp[0]; //计算MVD mdy = h->mb.cache.mv[i_list][x264_scan8[idx]][1] - mvp[1];
/* encode */ x264_cabac_mb_mvd_cpn( h, cb, i_list, idx, 0, mdx ); //编码 x264_cabac_mb_mvd_cpn( h, cb, i_list, idx, 1, mdy );
/* save value */ x264_macroblock_cache_mvd( h, block_idx_x[idx], block_idx_y[idx], width, height, i_list, mdx, mdy ); // 保存MVD } 4、firstime MV预测过程详解(附图) ===========第一步:确定相邻块=========== MV 预测以宏块分割(或亚宏块分割,如果宏块存在亚分割)为单位,同一个宏块分割(或亚宏块分割)内所有 4*4 块 MV 预测值相同。以每个宏块分割(或亚宏块分割)的左上角像素 pixel1 和右上角像素 pixel2 为参考点来确定相邻块则: pixel1 左侧相邻像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 A pixel1 上方相邻像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 B pixel2 右上对角线像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 C pixel1 左上对角线像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 D 图片附件: MV预测示意图.JPG (2006-9-29 11:14 AM, 85.25 K)
以最复杂的 8*8 宏块分割类型为例(此时只存在亚宏块分割),分析如下: 假设图中黑色框表示宏块、每个绿色框表示一个 4*4 块、每个红色框表示一个 8*8 块。当前宏块的宏块分割模式为 8*8(如图中红色线),其亚宏块分割模式分别为:第一个 8*8 块为 8*8,第二个 8*8 块为 4*4(如图中蓝色线),第三个 8*8 块为 4*8(如图中蓝色线),第四个 8*8 块为 8*4(如图中蓝色线)。则按照上述方法来确定相邻块的方法如下: 第一个预测对象为第一个 8*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 7 号 4*4 块,B 为 2 号 4*4 块,C 为 4 号 4*4 块,D 为 1 号 4*4 块。9、14、15 与 8 具有相同 MV 预测值 第二个预测对象为第二个 8*8 块的第一个 4*4 块,即 10 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 9 号4*4块,B 为 4 号4*4块,C 为 5 号 4*4 块, D 为 3 号 4*4 块 第三个预测对象为第二个 8*8 块的第二个 4*4 块,即 11 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 10 号4*4块,B 为 5 号4*4块,C 为 6 号 4*4 块,D 为 4 号 4*4 块 第四个预测对象为第二个 8*8 块的第三个 4*4 块,即 16 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 15 号4*4块,B 为 10 号4*4块,C 为 11 号 4*4 块,D 为 9 号 4*4 块 第五个预测对象为第二个 8*8 块的第四个 4*4 块,即 17 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 16 号4*4块,B 为 11 号4*4块,C 为 12 号 4*4 块,D 为 10 号 4*4 块 第六个预测对象为第三个 8*8 块的第一个 4*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 19 号 4*4 块,B 为 14 号 4*4 块,C 为 15 号 4*4 块,D 为 13 号 4*4 块。26 与 20 具有相同 MV 预测值 第七个预测对象为第三个 8*8 块的第二个 4*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 20 号 4*4 块,B 为 15 号 4*4 块,C 为 16 号 4*4 块,D 为 14 号 4*4 块。27 与 21 具有相同 MV 预测值 第八个预测对象为第四个 8*8 块的第一个 8*4 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 21 号 4*4 块,B 为 16 号 4*4 块,C 为 18 号 4*4 块,D 为 15 号 4*4 块。23 与 22 具有相同 MV 预测值 第九个预测对象为第四个 8*8 块的第二个 8*4 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 27 号 4*4 块,B 为 22 号 4*4 块,C 为 24 号 4*4 块,D 为 21 号 4*4 块。29 与 28 具有相同 MV 预测值 ===========第二步:确定 A、B、C 的可用性=========== 根据 A、B、C 所在宏块是否存在或者是否允许参与预测来判断。如果 C 不可用,采用 D 代替 C ===========第三步:预测 MV =========== 1、如果 A、B、C 三个参考块中只有一个与当前预测对象为同一参考帧,则选取该参考块的 MV 作为最终 MV 预测值 2、当前宏块是否为 8*16 或者 16*8 分割: (1)、如果当前宏块为 8*16 分割类型: 对于左边 8*16 分割,如果 A 与当前分割为同一参考帧,则采用 A 的 MV 为该分割的最终 MV 预测值 对于右边 8*16 分割,如果 C 与当前分割为同一参考帧,则采用 C 的 MV 为该分割的最终 MV 预测值 (2)、如果当前宏块为 16*8 分割类型: 对于上边 16*8 分割,如果 B 与当前分割为同一参考帧,则采用 B 的 MV 为该分割的最终 MV 预测值 对于下边 16*8 分割,如果 A 与当前分割为同一参考帧,则采用 A 的 MV 为该分割的最终 MV 预测值 3、其余情况并且 B、C 中有一个可用或者两者都可用,则采用中值预测(取 A、B、C 三者中MV的中值为最终 MV 预测值) 4、其余情况并且 B、C 皆不可用,则采用 A 的 MV 为最终 MV 预测值 【注】:1、宏块分割时的相邻块确定方法与第一步所述过程雷同:16*16 相当于 8*8,8*16、16*8 分别相当于 4*8、8*4 2、对于不可用的相邻块,其 MV 仍然可能参与 MV 预测,但其值为 0。例如:A 不可用,B、C 可用,则最终可能仍然是在 A、B、C 中取中值,但此时 A 的 MV 为 0; 3、对于不可用的相邻块,其参考帧索引被设置为 -1,即必然与当前预测对象非同一参考帧; 4、可以验证:同时满足第三步的第一、第二两种情况时,按第一种情况计算 MV 预测值与按第二种情况计算 MV 预测值等效; 5、该预测过程即为标准 8.4.1.3 小节的内容,在 JM86 中对应的代码为 SetMotionVectorPredictor 函数; 6、MBAFF 情况下的相邻块均指对应位置(co-locate)块。
常规选项设置 帧的相关设置 Psy算法与量化值 音频解码方式 |
|