H.264笔记

H.264标准写得比较繁复,所以考虑在浏览完Whitepaper之后就开始研读X264代码。X264代码风格还是比较清晰简洁的。

根据对标准得理解,Picture Order Count在Slice解码的一开始就被提及:

I0 B1 B2 P3 B4 B5 P6
I0 P3 B1 B2 P6 B4 B5

于是I0的POC是0,P3的POC是3,B1是1……

为了支持H264复杂的帧存机制,X264以专门的一个模块frame.c进行处理。

common/frame.c中包括一组帧缓冲操作函数。包括对帧进行FILO和FIFO存取,空闲帧队列的相应操作等。

以下逐个函数分析encoder.c中编码一帧的函数x264_encoder_encode中有关frame的调用:

x264_reference_update
这个函数里最主要的工作的是将上一个参考帧放入参考帧队列,并从空闲帧队列中取出一帧作为当前的参考工作帧(即解码操作的目的帧),即h->fdec。

x264_t结构体维护着CODEC的诸多重要信息,其中成员frames是一个指示和控制帧编码过程的结构。其中current是已经准备就绪可以编码的帧,其类型已经确定;next是尚未确定类型的帧;unused用于回收不使用的frame结构体以备今后再次使用。frames结构体中i_input指示当前输入的帧的(播放顺序)序号。i_delay设置为由B帧个数(线程个数)确定的帧缓冲延迟,在多线程情况下为i_delay = i_bframe + i_threads - 1。而判断B帧缓冲填充是否足够则通过条件判断:h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads。
x264_encoder_encode每次会以参数送入一帧待编码的帧pic_in,函数首先会从空闲队列中取出一帧用于承载该新帧,而它的i_frame被设定为播放顺序计数,如:fenc->i_frame = h->frames.i_input++。
x264_encoder_encode在根据上述判据确定B帧缓冲充满的情况下才进行后续编码工作。


当当前队列(current队列)可用帧为0时,需要对next队列中的帧进行判决,需要进行如下过程:
1. 调用x264_slicetype_decide
这个函数确定当前条带(帧)的类型
其中首先调用x264_ratecontrol_slice_type,依据码率控制逐个求出next列表中所有帧的类型(虽然在当前并不全部用到,见后)。
随后统计审查并调整next列表,保证IDR帧满足有关最大关键帧间隔的要求的正常出现:即针对frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max作判断。审查按顺序针对所有被判定为B系或AUTO类型的帧进行(这些帧在审核过程中被确认为B帧),直到遇到第一个不是这样的帧。
如果某个帧被指定为IDR,则一个GOP在它之前结束。

2. 而后,即将next列表中已经判定的一系列帧(先后是一些B帧和一个非B帧)转移到current列表中。在这个过程中:
原始序列(播放顺序)B0, B1, B2, P,转移后的顺序为P, B0, B1, B2。在使用bframe_pyramid模式时,中间的B帧要前置,即上述顺序变为:P, B1, B0, B2。

此时,就可以从current队列中取出一帧,进行编码,现在记这帧叫h->fenc。
首先做几项和帧有关的设置工作:
1. 如果f_enc是IDR,则将最近IDR序号标记h->frames.i_last_idr设置为i_frame。
2. 根据f_enc的类型确定NAL和SLICE类型相关参数。
3. 设置POC为2 * (h->fenc->i_frame - h->frames.i_last_idr)。并使得h->fdec和h->fenc的主要帧参数一致。

随后进行以下一些过程:
x264_reference_build_list
在这个函数中,我们将遇到参考帧列表h->frames.reference和H.264很有特色的双列表(h->fref0、h->fref1)。前者中放置了所有可用于参考的参考帧。
首先将所有reference列表中的帧按照POC和h->fenc的POC的大小关系不同复制到双列表中,其中h->fref0放置POC较小的那些。然后对双列表进行排序,使得h->fref0中的帧POC按从大到小排列,h->fref1按从小到大排列,于是这两个列表中靠前的帧的POC比较接近当前帧h->fenc。在这里,一个特殊的情况是,对于P帧(只需要用到列表h->fref0做参考),其排序依据是i_frame_num而不是POC,因此需要对这种情况的列表h->fref0置位需重排序标记。最后对双列表的长度做限制。

x264_ratecontrol_start
码率控制初始化,在以后专门的话题中论述。
在初始化后,从码率控制体中获得全局QP值:i_global_qp = x264_ratecontrol_qp( h )。

如果当前是B条带(帧),则调用x264_macroblock_bipred_init
该函数设置一些参考帧关系矩阵,关系矩阵以fref0的索引为行坐标,以fref1的索引为列坐标。主要包括:dist_scale_factor,bipred_weight,矩阵值主要由相应帧的POC决定。后续讨论。

x264_slice_init
内部调用x264_slice_header_init,它对Header进行配置,含义以后讨论。

bs_init
初始化码流工作上下文。

随后将帧类型表现到码流中。接着写SEI,SPS和PPS。

然后就是最关键的写条带(即主要的编码工作)开始的地方:
x264_slices_write
这里通过x264_stack_align调用x264_slice_write,为了将栈做4字节对齐,以提高运行效率。

x264_slice_write函数是编码一帧的关键函数,将在下一章中论述。

宏块结构

这里主要有一下几个过程:
1. 初始化h->stat.frame,即全部清零。
2. 写条带头:x264_slice_header_write,即把刚才x264_slice_header_init设置的一些参数写入。
3. 如果是CABAC编码,则初始化CABAC。有关CABAC在后续相关章节讨论。
4. 遍历一帧中的所有宏块,这是编码的主要部分:
for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; )
其中sh.i_first_mb和sh.i_last_mb在x264_slice_header_init中赋值,分别是0和宏块行列数乘积。
5. 最后输出码流尾部。
其间穿插一些必要的初始化和配置。

本章着重分析编码的主要部分,包括一下过程:
需要注意的是,这里讨论的宏块是固定尺寸的,即Y分量计为16x16。(而运动宏块的尺寸则是多变的)

1. 首先根据宏块的序号得出宏块的坐标。然后统计当前宏块的码流位置:
int mb_spos = bs_pos(&h->out.bs) + x264_cabac_pos(&h->cabac);

2. 然后出现一句:
if( i_mb_x == 0 )
x264_fdec_filter_row( h, i_mb_y );
这是对行进行去块过程。详细情况专门论述。

3. 随后是x264_macroblock_cache_load,这里主要进行当前宏块和相关信息的提取。这个函数比较大,也在专题中论述。

4. 接下去进行解码分析操作
x264_macroblock_analyse

5. 以及解码实体:
x264_macroblock_encode

6. 而后处理码流输出,也分CABAC和CAVLC两种情况。

7. 接着出现x264_macroblock_cache_save,和x264_macroblock_cache_load对应,保存处理完的宏块。

8. 更新宏块统计


下面,首先看编码主体x264_macroblock_encode的结构。
该函数按不同宏块类型情况进行不同的处理。包括如下类型:
P_SKIP
B_SKIP
I_16x16 - 一次完成一个块h->mb.pic.p_fdec[0]的编码。
用函数指针h->predict_16x16[i_mode]进行帧内预测
I_8x8 - 分4次完成。h->mb.pic.p_fdec[0][8 * (i&1) + 8 * (i>>1) * FDEC_STRIDE]指定了当前位置。即:
[0][1]
[2][3]
用函数指针完成4个宏块的帧内预测
I_4x4 - 分16次完成。h->mb.pic.p_fdec[0][4 * block_idx_x[i] + 4 * block_idx_y[i] * FDEC_STRIDE]指定当前位置。
根据block_idx_x和block_idx_y的定义,其顺序是:
[0][1][4][5]
[2][3][6][7]
[8][9][c][d]
[a][b][e][f]
用函数指针完成4个宏块的帧内预测
Inter - 首先进行运动补偿。见后续讨论。
然后分多种情形:
1. 无损压缩情况:h->mb.b_lossless == TRUE(必须采用4x4DCT)
根据上述I_4x4中描述的顺序遍历16个4x4块。
这里调用的函数是zigzag_sub_4x4_field,完成残差。(无损情况直接发送残差)
2. 指定采用8x8DCT情况:h->mb.b_transform_8x8 == TRUE
首先是作残差并DCT的函数:sub16x16_dct8。
然后对4个8x8的DCT系数块进行处理:包括可能的去噪x264_denoise_dct,量化(标量/矢量)。有关量化也在专题中论述。量化完成后进行系数扫描(对角平衡的普通扫描)。
3. 一般情况

接着处理色差分量,如果是Intra的则进行预测。
最后写Coded Block Pattern。

帧内预测

环路内滤波

H.264环路内滤波顾名思义在编码侧开启后解码部分必须跟随开启,因此是该视频编码方案的不可分割的组成部分。

以下整理了Baseline情形下环路滤波的四种情形:

All cases that may exist for Baseline
Bs = 4: either is intra, MB edge
Bs = 3: either is intra, block edge
Bs = 2: both are inter, either is coded
Bs = 1: both are inter neither is coded, different ref pictures, either MV component is no smaller than 4
Bs = 0: o.w.



以T264为例:
环路滤波入口函数以光栅扫描顺序对所有宏块实施deblock_mb。

deblock_mb完成一个宏块的滤波,它包含以下步骤:

1. 水平相邻宏块滤波,即对与mb_xy-1宏块的边界滤波,以及内部3条纵向块边界滤波;

2. 垂直相邻宏块滤波,即对与mb_xy-mb_stride宏块的边界滤波,以及内部3条横向块边界滤波;



每条边界长度均为16个像素,等分成4份,每个对应一个最小块大小的边界,以此为单元进行滤波。
对每条边界调用get_strength,获得这4个部分的Bs值。

本章讨论的代码主要位于common/predict.c中。
x264_macroblock_cache_load函数在每个宏块解码之前初始化某些状态,在x264_slice_write函数的宏块处理循环中被调用。
i_mb_xy: 当前宏块的索引
i_mb_4x4: 当前宏块中第一个4x4块的索引
i_mb_8x8: 当前宏块中第一个8x8块的索引
i_top_y: 上方宏块的y索引
i_top_xy: 上方宏块的索引
i_top_4x4: 当前宏块中第一个4x4块上方的4x4块的索引
i_top_8x8: 当前宏块中第一个8x8块上方的8x8块的索引

这里首先初始化和当前宏块毗邻的已解码块。
这里用到x264_scan8,它指示如下扫描结构:

0 1 2 3 4 5 6 7
0
1 0 1 0 1 4 5
2 2 3 2 3 6 7
3 8 9 C D
4 0 1 A B E F
5 2 3

在空缺部分恰好可以填入相应宏块的毗邻块。

以下逐个各种分析预测情况:

对于16x16的块:
predict_16x16
共7种预测模式:H, V, DC, P, LEFT, TOP, 128
predict_16x16_h
用左侧相邻16x16块的右侧像素沿水平方向覆盖
predict_16x16_v
用上方相邻16x16块的底行像素沿竖直方向覆盖
predict_16x16_p
右上左下均值预测覆盖(注意均值生成)
predict_16x16_dc
用左侧边沿和上方边沿的均值作为DC预测值进行单一覆盖
predict_16x16_dc_left
只用左侧边沿进行DC单一覆盖
predict_16x16_dc_top
只用上方边沿进行DC单一覆盖
predict_16x16_dc_128
用128进行单一覆盖

对于8x8的色差块:
predict_8x8c_dc_128
用128进行单一覆盖
predict_8x8c_dc_left
上下两个4行分别以对应左边沿4个像素均值单一覆盖
predict_8x8c_dc_top
左右两个4列分别以对应上边沿4个像素均值单一覆盖
predict_8x8c_dc
s0 s1
s2 b0 b1
s3 b2 b3
s?是边沿4像素组
4个4x4块分别用s0,s2均值,s1均值,s3均值和s1,s3均值单一覆盖
predict_8x8c_h
用左侧相邻8x8块的右边沿像素沿水平方向覆盖
predict_16x16_v
用上方相邻16x16块的底行像素沿竖直方向覆盖
predict_8x8c_p
右上左下均值预测覆盖(注意均值生成)

对于4x4的块:(这是H264文档经常拿来作demo的)
predict_4x4_dc_128
用128进行单一覆盖
predict_4x4_dc_left
用左边沿4个像素均值单一覆盖
predict_4x4_dc_top
用上边沿4个像素均值单一覆盖
predict_4x4_dc
用左侧和上方边沿共8个像素均值单一覆盖
predict_4x4_h
用左侧相邻4x4块的右边沿像素沿水平方向覆盖
predict_4x4_v
用上方相邻4x4块的底行像素沿竖直方向覆盖
predict_4x4_ddl
右上至左下预测覆盖(注意均值生成)
predict_4x4_ddr
左上至右下预测覆盖(注意均值生成)
predict_4x4_vr
左上到右下(偏下)预测覆盖(注意均值生成)
predict_4x4_hd
左上到右下(偏右)预测覆盖(注意均值生成)
predict_4x4_vl
右上到左下(偏下)预测覆盖(注意均值生成)
predict_4x4_hu
左下到右上(偏右)预测覆盖(注意均值生成)

对于8x8亮度块:
edge中存放
predict_8x8_dc_128
用128进行单一覆盖
predict_8x8_dc_left
用左侧块边沿8个像素均值单一覆盖
predict_8x8_dc_top
用上方块边沿8个像素均值单一覆盖
predict_8x8_dc
用左侧块和上方块边沿共16个像素均值单一覆盖
predict_8x8_h
用左侧边沿预测覆盖
predict_8x8_v
用上方边沿预测覆盖
predict_8x8_ddl
右上到左下预测覆盖(注意均值生成)
predict_8x8_ddr
左上到右下预测覆盖(注意均值生成)
predict_8x8_vr
左上到右下(偏下)预测覆盖(注意均值生成)
predict_8x8_hd
左上到右下(偏右)预测覆盖(注意均值生成)
predict_8x8_vl
右上到左下(偏下)预测覆盖(注意均值生成)
predict_8x8_hu
左下到右上(偏右)预测覆盖(注意均值生成)

以上“注意均值生成”指预测值一般由起点决定,起点由反向延长线确定,起点2倍权重和两侧点构成均值。
例如45度均值为以起始点对应45度位置的点作为中心(2倍权重),其两侧点作为补充形成的均值。

H.264标准规定,首先由macroblock层的mb_type导出intra块的预测类型macroblock prediction mode,其中包含子块尺度信息,有4x4,8x8和16x16三种。4x4和8x8按照标准由变换尺寸区分,4x4是最常用的模式。
在确定预测类型之后,对于含16个子块的4x4模式,每个子块的预测模式(即上述讨论)由标准规定的判决算法给出:
<...>
H264源码分析(一) (一)句法元素的分层结构             在 H.264 定义的码流中,句法元素被组织成有层次的结构,分别描述各个层次的信息,如下图所示               在H.264 中,句法元素共被组织成  序列、图像、片、宏块、子宏块五个层次。           在这样的结构中,每一层的头部和它的数据部分形成管理与被管理的强依赖关系,头部的句法元素是该层数据的核心,而一 阅读详情

相关推荐

H264源码分析(四)

(十二)子宏块预测句法 sub_mb_pred( mb_type ) {       for( mbPartIdx = 0; mbPartIdx         // sub_mb_type[ mbPartIdx ]    指明子宏块的预测类型,在不同的宏块类型中这个句法元素的语义不一样。              sub_mb_type[ mbPartIdx ]    

zhanjianshinian的专栏 1833

POC and frame_num 详细解析,清晰易懂

POC and frame_num 详细解析,清晰易懂注意,H264码流的输出顺序是编码顺序,所以在编码B帧的时候,由于B是双向预测,需要先编码后面编码帧P/I,这时候先输出I/P,后面才有B帧,我们在解码段拿到相应的I/P帧后,不能马上丢到buffer list里面,还需要等待后面的B帧,解码B帧后再reorder.所以相应产生了两个参数。poc, frame_num,这两个元素在slice级输出

H.264笔记整理3

H.264整理3 转自:http://www.hellotongtong.com/avcodecbase/avcodecbase-h264-learning120.html 需求 在移动端做音视频开发不同于基本的UI业务逻辑工作,音视频开发需要你懂得音视频中一些基本概念,针对编解码而言,我们必须提前懂得编解码器的一些特性,码流的结构,码流中一些重要信息如sps,pps,vps,start code以及基本的工作原理,而大多同学都只是一知半解,所以导致代码中的部分内容虽可以简单理解却...

福优学苑@音视频+流媒体 1342

FPGA纯verilog代码实现H264视频压缩 提供工程源码和技术支持

FPGA纯verilog代码实现H264视频压缩 提供工程源码和技术支持 H264视频压缩与解码在FPGA图传领域应用广泛,Xilinx高端器件已经内嵌了H264加速器,在Linux系统下调用API即可使用,但对于需要定制私有算法或者协议的H264视频压缩与解码应用或者学习研究者而言,纯verilog代码实现H264视频压缩依然具有实用价值,本设计采用纯verilog代码实现H264视频压缩,没有使用任何IP,具有参考价值; 本文详细描述了FPGA纯verilog代码实现H264视频压缩的设计方案,工程代

qq_41667729的博客 5725

H.264中POC类型之探讨

<br />有 B 图像的场合。POC 表示的是图像显示顺序。由于POC对于参考序列的初始化,重排序及标记关系重大,所以做了如下的分析,以下讨论情况是针对帧编码。<br /><br />pic_order_cnt_type=0的时候:<br />       poc与frame_num没有直接的关系,是显式地出现在bit流中为pic_order_cnt_lsb(PicOderCntMsb 和 PicOderCntLsb分别代表的是POC的高位和低位。H.264中为了提高压缩效率,只对POC的低位进行编码传输

szu030606的专栏 1722

H.264 编码笔记

H.264 视频编码技术解析

m0_60856753的博客 1648

H.264笔记之一

H.264标准写得比较繁复,所以考虑在浏览完Whitepaper之后就开始研读X264代码。X264代码风格还是比较清晰简洁的。 根据对标准的理解,Picture Order Count在Slice解码的一开始就被提及: I0 B1 B2 P3 B4 B5 P6 I0 P3 B1 B2 P6 B4 B5 于是I0的POC是0,P3的POC是3,B1是1…… 为了支持H264复杂的帧存机制

quanben 6077

H.264学习笔记

H.264凭借其出色的压缩效率、灵活的编码方式以及高质量的视频表现,成为了现代视频技术的核心标准之一。它不仅减少了存储需求,还提高了流媒体和实时视频传输的可行性。对于从高清到超高清视频,H.264的编码能力仍然广泛适用。H.264的帧结构设计(I帧、P帧和B帧)极大地提高了视频编码的压缩效率。I帧保存完整的图像信息,P帧通过参考之前的帧减少冗余数据,而B帧则通过双向参考提供进一步的压缩优势。这种帧间的协同工作大幅降低了带宽需求和存储成本,同时保持了视频的高质量。NALU头。

zfenggo的博客 1193

h.264优化笔记

目前 H.264编解码器的实现可以采用以下几种方式 Ø 采用奔腾 Pentium 四代机实现 H.264 编解码最早就是在 PC 平台上实现的 由于简单易开发 基于该平台的研究得到最多 JVT 的 JM 参考代码是就是基于 PC 平台的 此方案的优点是利用当前最新的 PC 资源 以及较强的软件工具 Intel 的 SSE2 和 MMX 提供了较完整的多媒体指令集和流水线 其缺点是占用资源

qq_27901569的博客 449

H.264笔记之三——帧内预测

本章讨论的代码主要位于common/predict.c中。x264_macroblock_cache_load函数在每个宏块解码之前初始化某些状态,在x264_slice_write函数的宏块处理循环中被调用。i_mb_xy:   当前宏块的索引i_mb_4x4:  当前宏块中第一个4x4块的索引i_mb_8x8:  当前宏块中第一个8x8块的索引i_top_y:   上方宏块的y索引i_top_

quanben 4347

H.264+JM学习笔记

H.264特点H.264支持4:2:0的连续或隔行视频的编码和解码。H.264基本概念1、宏块 在视频编码中,一个编码图像通常划分成若干宏块组成,一个宏块由一个亮度像素块和附加的两个色度像素块组成。一般来说,亮度块为16x16大小的像素块,而两个色度图像像素块的大小依据其图像的采样格式而定,如:对于YUV420采样图像,色度块为8x8大小的像素块。每个图象中,若干宏块被排列成片的形式,视频

上官宏竹的专栏 5109

H.264笔记之三——环路内滤波

    H.264环路内滤波顾名思义在编码侧开启后解码部分必须跟随开启,因此是该视频编码方案的不可分割的组成部分。    以下整理了Baseline情形下环路滤波的四种情形:    All cases that may exist for Baseline    Bs = 4: either is intra, MB edge    Bs = 3: either is intra, blo

quanben 2661
上一篇: H264学习方法&&历程&&资料
下一篇: 产品技术和管理
xpp02
博客等级 码龄16年 55粉丝 288原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值