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模式,每个子块的预测模式(即上述讨论)由标准规定的判决算法给出:
<...>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/453756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进制转换中dbho是什么意思_什么是网段?二进制十进制如何互相转换?看完这篇,你就全明白了...

之前的文章讲了ip&#xff0c;子网掩码&#xff0c;网关的关系&#xff0c;今天着重讲一下网段。我们用傻瓜交换机通讯时&#xff0c;一个网段的设备才能互相通讯&#xff0c;怎么能判断两个ip是同一个网段呢&#xff1f;今天就简单的说一下。(这篇文章用语音听可以起到催眠作用…

【网络流24题】星际转移问题(最大流)

【网络流24题】星际转移问题&#xff08;最大流&#xff09; 题面 Cogs 题解 因为天数是未知的&#xff0c;所以我们要想办法处理天数 可以选择二分或者依次累加天数 因为数据范围较小&#xff0c;使用二分可能反而复杂度会增高 所以使用不断累加天数 那么&#xff0c;把所有的…

使用 gunicorn 部署flask项目

1、WSGI协议 Web框架致力于如何生成HTML代码&#xff0c;而Web服务器用于处理和响应HTTP请求。Web框架和Web服务器之间的通信&#xff0c;需要一套双方都遵守的接口协议。WSGI协议就是用来统一这两者的接口的。 2、WSGI容器 常用的WSGI容器有Gunicorn和uWSGI&#xff0c;但G…

软件需求与问题解决

&#xff08;一&#xff09; 小满当上项目经理后不久&#xff0c;参与了一个大项目。当时市场签下来的时候&#xff0c;公司里面是欢天喜地的。项目做了一年多。到了交付的时候&#xff0c;用户却很不满意&#xff0c;当初说好的东西&#xff0c;好多都变了卦。用户是上帝&…

flex 换主轴后子元素占满_Chrome72 嵌套 flex 布局修改,你的网站可能会发生布局错乱...

起源2019 年 1 月 29 日&#xff0c;Chrome72 正式版(72.0.3626.81)发布&#xff0c;本次发布带来了一个改变&#xff0c;且没有在更新日志中提及&#xff0c;该改变导致某些网站发生了布局错乱。该改变主要针对的是嵌套的flex布局&#xff0c;下面我们一起看下是怎么回事。问题…

使用 Django + Wusgi + Nginx 部署 Django

如何在生产上部署Django? Django的部署可以有很多方式&#xff0c;采用 nginxuwsgi 的方式是其中比较常见的一种方式。 uwsgi介绍 uWSGI是一个Web服务器&#xff0c;它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。 WSGI / …

网络学习网址

网络之路博客 http://ccieh3c.com/ 转载于:https://www.cnblogs.com/changha0/p/8179801.html

路由到另外一个页面_Nextjs使用解读一(项目搭建与路由系统)

文章说明&#xff1a;1. 之前想搭建个人博客&#xff0c;由于学习的是react技术栈&#xff0c;所以就到处搜罗资料学了nextjs&#xff0c;配合koa就把博客搭起来了。该系列文章基于我的学习笔记&#xff0c;重新整理了一遍&#xff0c;如果有错误之处&#xff0c;还请指正。2. …

微信获取token -1000

最终翻看微信开发api找到需要去配置IP白名单。只需要配置访问来源IP即可。 转载于:https://www.cnblogs.com/yangjinqiang/p/8184663.html

产品技术和管理

为啥纯粹为消费者传递体验的活动可以价格不菲&#xff0c;几为暴利&#xff1f;——谈客户体验作为客户价值提升之源 不论产品还是服务&#xff0c;如果能够为消费者传递有益的体验&#xff0c;其价值就可以在一般的产品服务之上得以体现&#xff1b;附加了体验的产品&#xff…

Linux 修改系统编码

linux服务器的字符集设置可能影响到网站页面出现 “&#xff1f;&#xff1f;&#xff1f;” 等问号乱码&#xff0c;还有可能导致文件中的汉字部分出现乱码。有两个原因 服务器没有安装 zh_CN.UTF-8 字符集&#xff0c;导致不支持中文&#xff01;服务器虽然装了 zh_CN.UTF-8…

jquery ztree 设置勾选_047 JAVA-jQuery

jQuery操作元素属性的值表单:<body><input type"button" name"" id"but1" value"测试获得属性值" /><hr />账号&#xff1a;<input type"text" name"sxtzh" id"zhanghao" value&q…

Opencv undefined reference to `cv::imread() Ubuntu编译

Ubuntu下编译一个C文件&#xff0c;C源程序中使用了opencv&#xff0c;opencv的安装没有问题&#xff0c;但是在编译的过程中出现如下错误&#xff1a; undefined reference to cv::imread(std::string const&, int)undefined reference to cv::noArray()undefined referen…

深度学习目标检测之 YOLO v1

这是继 RCNN&#xff0c;fast-RCNN 和 faster-RCNN 之后&#xff0c;rbg&#xff08;RossGirshick&#xff09;针对DL目标检测速度问题提出的另外一种框架。YOLO V1 增强版本GPU中能跑45fps&#xff0c;简化版本155fps。 论文名&#xff1a; 《You Only Look Once&#xff1a;…

编程珠玑番外篇

1.Plan 9 的八卦 在 Windows 下喜欢用 FTP 的同学抱怨 Linux 下面没有如 LeapFTP 那样的方便的工具. 在苹果下面用惯了 Cyberduck 的同学可能也会抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻烦的事情. 其实一点都不麻烦, 因为在 LINUX 系统上压根就不需要用 FTP. 为什么呢? 因…

BT下载原理分析

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 BitTorrent协议。 BT全名为BitTorrent,是一个p2p软件,你在下载download的同时&#xff0c;也在为其他用户提供上传upload&#xff0c;因为大家是“互相帮助”&#xff0c;所以不会随着用户数的增加而…

表格列求和_excel表格制作,Excel表格的基本操作,包含制作一个表格10方面的知识...

创建表格&#xff0c;插入与删除一行一列或多行多行&#xff0c;一次移动一行一列或多行多列&#xff0c;拆分与合并单元格&#xff0c;单元格内换行&#xff0c;表格求和与求平均值是Excel表格的基本操作&#xff1b;除此之外&#xff0c;Excel表格的基本操作还包括调整行高列…

深度学习之 FPN (Feature Pyramid Networks)

论文题目&#xff1a;Feature Pyramid Networks for Object Detection论文链接&#xff1a;https://arxiv.org/abs/1612.03144论文代码&#xff1a;Caffe版本 https://github.com/unsky/FPN 《Feature Pyramid Networks for Object Detection》这篇论文主要解决的问题是目标检…

ISLR—第二章 Statistical Learning

Statistical Learning Y 和X的关系why estimate f 用来预测 预测的时候可以将f^当成一个black box来用&#xff0c;目的主要是预测对应x时候的y而不关系它们之间的关系。用来推断 推断的时候&#xff0c;f^不能是一个black box&#xff0c;因为我们想知道predictor和response之…

提高编程思想

虚函数和抽象函数有什么区别 虚函数是有代码的并明确允许子类去覆盖&#xff0c;但子类也可不覆盖,就是说可以直接用&#xff0c;不用重写 抽象函数是没有代码&#xff0c;子类继承后一定要重写 ****************************************************************** 在一…