本章讨论的代码主要位于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模式,每个子块的预测模式(即上述讨论)由标准规定的判决算法给出:
帧内预测
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值。
环路内滤波
这里主要有一下几个过程:
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标准写得比较繁复,所以考虑在浏览完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函数是编码一帧的关键函数,将在下一章中论述。
相关推荐
《H.264学习笔记-JM源代码笔记》是一份深入探讨H.264编码标准和基于JM(JVT-Matrix)源代码的学习资料。H.264,全称为Advanced Video Coding(高级视频编码),是国际电信联盟(ITU-T)的VCEG(视频编码专家组)和...
这份“H.264阅读笔记”资料应该包含了对H.264编码技术的深入解析,帮助读者理解其工作原理和技术细节。 H.264编码技术的核心目标是在有限的带宽下提供高质量的视频传输。它采用了多项创新技术来实现这一目标: 1. ...
1. **我的H.264学习历程.doc**:这可能是作者个人学习H.264的笔记或经验分享,可能包含了学习路径、遇到的问题和解决方案,对于初学者来说是一份宝贵的参考资料。 2. **中文版白皮书H.264MPEG4_Part_10_White_Paper...
《H.264中英文对照手册》是一个极具价值的资源,主要涵盖了H.264视频编码标准的详细信息,同时提供了中文和英文的对照解释,方便学习者深入理解和应用这一技术。H.264,也被称为AVC(Advanced Video Coding),是...
h.264源代码分析笔记,详细的介绍了源代码的过程。把源代码的主要函数,与每个函数实现的功能。每个函数的变量代表的意思,进行了详细的介绍。能让初学者事半功倍,笔记倾情奉献!本人花费半年的时间的进行代码调试...
MPEG4和H.264是两种广泛应用于视频编码的国际标准,它们在数字视频处理、编码和解码领域占据重要地位。这两者都是为了高效地压缩视频数据,以便在网络传输和存储中节省资源。 视频编码是将数字视频信号转化为可传输...
这里是我在研究H.264 的心得,我把H.264代码的整个体系的功能全部研究之后,把H.264 的程序运行流程,与H.264 的主要函数代表的意思一并收入在里面!相信,我这一年的学习实践,一定能给你带来事半功倍的效果!
这份PDF文档高清可编辑,方便读者做笔记、标记重点,对于学习H.264标准的理论知识和实践应用非常有帮助。通过深入理解和实践这些技术,可以提升在视频处理领域的专业技能。 总的来说,《新一代视频压缩编码标准H....
标题:H.264/MPEG-4 Part 10 白皮书 描述:介绍H.264框架,这是视频压缩技术的基础知识,对于初学者至关重要。 ### H.264/MPEG-4 Part 10: 高级视频编码 (AVC) H.264/MPEG-4 Part 10,也被称为高级视频编码(AVC...
在深入探讨H.264(也称为MPEG-4 AVC)中的序列参数集(SPS,Sequence Parameter Set)之前,我们首先需了解H.264作为一种视频压缩标准,在现代视频编码技术中的核心地位。H.264旨在提供高质量的视频压缩,同时降低...
在H.264视频编码标准中,Picture Parameter Set (PPS) 是一个至关重要的组成部分,它包含了用于解码图像序列所需的一些关键参数。本文将深入解析PPS中的各个成员值,帮助理解H.264编码的工作原理。 1. pic_...
【标题】和【描述】提到的是富士通半导体发布的新一代转码器产品,这些转码器具有内置存储器并且支持H.264码率转换功能。这三款产品型号分别为MB86M01、MB86M02和MB86M03,它们都是双向H.264/MPEG-2转码器,能够处理...
本应用笔记还详细描述了H.264视频压缩技术在IP摄像机网络中的应用,简单介绍了Maxim的Mobicam3 IP摄像机参考设计。 引言 闭路电视系统(CCTV)中的IP摄像机按照互联网协议(IP)通过以太网链路传输音频、视频数据...
本应用笔记还详细描述了H.264视频压缩技术在IP摄像机网络中的应用,简单介绍了Maxim的Mobicam3 IP摄像机参考设计。 引言 闭路电视系统(CCTV)中的IP摄像机按照互联网协议(IP)通过以太网链路传输音频、视频数据...
【描述】:“初学H.264的朋友可以根据阅读笔记的方式来阅读代码,并结合H.264标准学习H.264。” 对于初学者来说,阅读JM代码可能会显得有些困难,因为它的设计目标是实现标准而非易读性。不过,通过阅读笔记,你...
摘要:本应用笔记介绍了当前视频监控系统所采用的不同压缩技术以及一些视频压缩标准的优势,讨论了利用传统CCTV和IP摄像机输入实现数字录像的基本原理,即采用H.264视频压缩标准能够有效提供数据存储容量,即使采用...
【描述】:JM代码阅读笔记是一份全面解析H.264编码标准实现的文档,它详细介绍了H.264 JM(JVT-M050)参考代码的结构、功能和实现细节。这份笔记涵盖了从基本的视频编码原理到复杂的熵编码算法,旨在帮助读者深入...
本文针对H.264到高效视频编码(HEVC)转码的技术挑战,提出了一种分层的长短期记忆网络(LSTM)方法来预测编码单元(CU)的拆分。文章首先分析了CU拆分模式与H.264特性之间的相关性。基于这些分析,进一步提出了一种...