承接上一篇文章:
一、前言:
1、H.265概述:
数字视频的超高清潮流奔腾向前,帧率从30 fps向60fps、120fps甚至240fps进发,与此同时,物理媒 介日薄西山,内容正通过有形无形的网络在世界各个角落的终端设备上传递。高度密集的数据给带宽和 存储带来巨大挑战,当前主流的H.264开始不敷应用,而新一代视频编码标准H.265似乎成为了数字4K 时代的"救世主"。
H.265又称为HEVC(全称High Efficiency Video Coding,高效率视频编码,本文统称为H.265),是 ITU-T H.264/MPEG-4 AVC标准的继任者。2004年由ISO/IEC Moving Picture Experts Group(MPEG)和 ITU-T Video Coding Experts Group(VCEG)作为ISO/IEC 23008-2 MPEG-H Part 2或称作ITU-T H.265开 始制定。第一版的HEVC/H.265视频压缩标准在2013年4月13日被接受为国际电信联盟(ITU-T)的正式标准。
从 H.265 : High efficiency video coding 能获取最新的关于H.265的编码文档pdf。
理论上H.265比H.264效率提高30-50%(尤其是在更高的分辨率情形下)。

2、基于块的视频编码技术:
H.265重新利用了H.264中定义的很多概念。两者都是基于块的视频编码技术,所以它们有着相同的根 源,和相近的编码方式,包括:
- 以宏块来细分图片,并最终以块来细分。
- 使用帧内压缩技术减少空间冗余。
- 使用帧内压缩技术减少时间冗余(运动估计和补偿)。
- 使用转换和量化来进行残留数据压缩。
- 使用熵编码减少残留和运动矢量传输和信号发送中的最后冗余。
3、H.264与H.265选用原则:
当你考虑"只是在普通互联网上传输4K内容,还是要实现最好的图像质量"之时,就要先厘清"更多的压缩" 和"更好的压缩"这两个概念。如果只是更多的压缩,4K和超高清不一定要保证比今天的1080p或HD做到更好的图片质量。更好的压缩则意味着更聪明的压缩,面对同样的原始素材,更好的压缩会以更好的方 式,在不牺牲质量的情况下令数据量减少。更多的压缩很容易,而更好的压缩需要更多的思考和更好的 技术,通过更智能的算法来处理图像,在维持质量的同时保持更低的比特率,这正是H.265所要做的。

如何实现更好的压缩,举例来讲,我们通常会发现在很多的图像素材里,如视像会议或者电影的很 多场景中,每一帧上的大部分内容并没有改变太多,视像会议中一般只有讲话者的头在动(甚至只有嘴唇 在动),而背景一般是不动的,在这种情况下,我们的做法不是对每一帧的每一个像素编码,而是对最初的帧编码,然后仅对发生改变的部分进行编码。

4、H.265压缩主要改变:
H.265正从以下几个方面向着"更好的压缩"迈进:
1)、图像分区:
H.265将图像划分为"树编码单元(coding tree blocks, CTU)",而不是像H.264那样的16×16的宏块。根据不同的编码设置,树编码块的尺寸可以被设置为64×64或有限的32×32或16×16。很多研究都展示出更大的树编码块可以提供更高的压缩效率(同样也需要更高的编码速度)。每个树编码块可以被递归分割,利用四叉树结构,分割为32×32、16×16、8×8的子区域,下图就是一个64×64树编码块的分区示例。每个图像进一步被区分为特殊的树编码块组,称之为切割(Slices)和拼贴(Tiles)。编码树单元是H.264的基本编码单位,如同H.264的宏块。编码树单元可向下分区编码单元(Coding Unit, CU)、预测单元(Prediction Unit,PU)及转换单元(Transform Unit,TU)。

每个编码树单元内包含1个亮度与2个色度编码树块,以及记录额外信息的语法元素。一般来说影片 大多是以YUV 4:2:0色彩采样进行压缩,因此以16 x 16的编码树单元为例,其中会包含1个16 x 16的亮 度编码树区块,以及2个8 x 8的色度编码树区块。

编码单元是H.265基本的预测单元。通常,较小的编码单元被用在细节区域(例如边界等),而较大的 编码单元被用在可预测的平面区域。

2)、转换尺寸:
每个编码单元可以四叉树的方式递归分割为转换单元。与H.264主要以4×4转换,偶尔以8×8转换所不同的是,H.265有若干种转换尺寸:32×32、16×16、8×8和4×4。从数学的角度来看,更大的转换单元可以更好地编码静态信号,而更小的转换单元可以更好地编码更小的"脉冲"信号。

3)、预测单元:
在转换和量化之前,首先是预测阶段(包括帧内预测和帧间预测)。
一个编码单元可以使用以下八种预测模式中的一种进行预测。

即使一个编码单元包含一个、两个或四个预测单元,也可以使用专门的帧间或帧内预测技术对其进行预 测,此外内编码的编码单元只能使用2N×2N或N×N的平方划分。间编码的编码单元可以使用平方和非对 称的方式划分。
- 帧内预测:HEVC有35个不同的帧内预测模式(包括9个AVC里已有的),包括DC模式、平面 (Planar)模式和33个方向的模式。帧内预测可以遵循变换单元的分割树,所以预测模式可以应用于 4×4、8×8、16×16和32×32的变换单元。
- 帧间预测:针对运动向量预测,H.265有两个参考表:L0和L1。每一个都拥有16个参照项,但是唯一图片的最大数量是8。H.265运动估计要比H.264更加复杂。它使用列表索引,有两个主要的预测模式:合 并和高级运动向量(Merge and Advanced MV.)。
在编码的过程,预测单元是进行预测的基本单元,变换单元是进行变换和量化的基本单元。这三个 单元的分离,使得变换、预测和编码各个处理环节更加灵活。
4)、去块化:
与H.264在4×4块上实现去块化所不同的是,HEVC的只能在8×8网格上实现去块。这就能允许去块 的并行处理(没有滤波器重叠)。首先去块的是画面里的所有垂直边缘,紧接着是所有水平边缘。与 H.264采用一样的滤波器。
采样点自适应偏移(Sample Adaptive Offset);
去块之后还有第二个可选的滤波器,叫做采样点自适应偏移。它类似于去块滤波器,应用在预测循 环里,结果存储在参考帧列表里。这个滤波器的目标是修订错误预测、编码漂移等,并应用自适应进行偏移。
5)、并行处理:
由于HEVC的解码要比AVC复杂很多,所以一些技术已经允许实现并行解码。最重要的为拼贴和波前 (Tiles and Wavefront)。图像被分成树编码单元的矩形网格(Tiles)。当前芯片架构已经从单核性能 逐渐往多核并行方向发展,因此为了适应并行化程度非常高的芯片实现, H.265 引入了很多并行运算的 优化思路。

6)、总结:
总而言之,HEVC将传统基于块的视频编码模式推向更高的效率水平,总结一下就是:
- 可变量的尺寸转换(从4×4 到32×32);
- 四叉树结构的预测区域(从64×64到4×4);
- 基于候选清单的运动向量预测;
- 多种帧内预测模式;
- 更精准的运动补偿滤波器;
- 优化的去块、采样点自适应偏移滤波器等;
5、H.265面临的挑战:
与之前从H.261到H.264的其他标准相比,H.265的显著改善不仅表现在帧间压缩领域,还表现在帧内压 缩方面。由于可变量的尺寸转换,H.265在块压缩方面有很大的改善,但是增加压缩效率的同时也带来 了一些新挑战。
视频编码是一个复杂的问题,对于内容的依赖性很高。众所周知,有静态背景的和高亮的低动态场景可以比高动态、黑场的图片进行更多的压缩。所以对于像H.264这样的现代化编解码器来说首要解决的是最困难的场景/情境。例如,有细节的关键帧、高动态的"勾边(crisp)"图像、黑暗区域的慢动态、 噪声/纹理等。
H.265在帧内编码方面效率更高,所以细节区域可以被编码得更好,在平滑区域和渐变区域也是如此。与H.264相比,H.265的运动估计和压缩更有效,而且在伪影出现前可以在更低的比特率上操作。好 消息是,H.265产生的伪影更加"平滑",质量的降低也非常协调,即便对非常激进的分辨率/比特率编码 时,也观感良好。
然而,正如硬币的两面,当处理黑暗区域的慢动态和噪声/纹理两种问题时,H.265的优势也会变成弱势。黑暗区域和噪声/纹理要求更精确的高频保留和更小的色阶变化。这通常被称之为编码的心理优化。
由于H.264使用小的转换,可以轻松将量化误差变成特征/细节,虽然与原始内容不同,但是感觉上 "近似"。接近原生频率范围的误差生成可以通过小的边界转换来阻止,因此也更加可控。而更大转换的H.265要使用这种方式则会更加复杂。
H.265编码视频的存储依然是个问题,即使蓝光光盘协会正在寻求一个能够在蓝光光盘上存储4K视频的解决方案。只有至少达到100GB容量的光碟才能存储H.264编码的蓝光4K电影。而另一方面,即使 H.265编码和芯片部件已经准备就绪,但是仍然缺少支持4K内容的存储和重放解决方案,并且能够兼容 现有的蓝光标准。这也是H.265发展中的一个主要挑战。
6、H.265 PK VP9:
在H.265大步向前的同时,谷歌VP8的继任者VP9也已推出,同样在VP8的基础上号称编码效率提高 50%,支持8K内容。VP9是一个开源和免费的规格,是WebM架构的一部分。谷歌已经在Chrome浏览器和YouTube中整合支持VP9。
与H.265在表面上类似,它同样可以抓取64×64个超级块。但265不同的是,它不一定是平方形式 的,所以它可以以64×32或4×8的块来采样,实现更大的效益。但另一方面,它只有10个预测模式来重建它们。
两者都很大程度上简化了现有这些格式,尽管实现了相近的文件尺寸,有初步的报告认为,H.265 有更高的图像质量,而VP9对于流媒体来说更加可靠。H.265更大的预测模型实现了边缘可视化,而VP9 实施更严格的编码规则,似乎可以让流媒体更加连贯和可靠。
H.265与VP9的比较有一点类似于HDMI与DisplayPort的比较。后者以版权免费的方式去争取一席空 间,但是前者的无处不在的应用意味着它会有更广泛的行业支持。这也是之前H.264轻松打败VP8的原因。
与此同时,第三个压缩格式也在规划之中,Xiph.Org基金会开发了"Daala",虽然它还比较遥远,但 是Xiph称其将是性能超越H.265和VP9的新一代规格。
7、H.265的未来:
高像素数量导致需要更复杂的编解码器来最小化带宽需求。持续连接PC或TV,平滑处理4K信号的最小码 流是20Mbit/s,例如Netflix要求用户的互联网连接至少提供持续的25Mbit/s带宽量。20到25Mbit/s代表 带宽的巨大改善,原生的、非压缩的4K视频需要在60Mbit/s的带宽上才会有好的表现。
对于大多数的行业应用来说,H.265就是解决这一问题的答案之一,但是也要付出一定代价:显著增加的算法复杂性据说需要10倍目前2K部署所用H.264编解码器的计算能力来支撑,而提供这种能力所需的硅也远非一个简单的商品条目。
很多制造商希望在上游芯片和IC技术供应商的努力之下,解决成本和功能不平衡的问题,让H.265 快速取代H264。就目前来看,H.265在广电领域已经有比较好的发展,但是否也会成为专业应用领域的主流规范还存有疑问。因为安防监控领域等专业领域不仅受制于上述挑战,而且还要看终端用户。对于 项目化的专业用户和需要监控的一般消费者而言,平安城市、交通检测和银行监控这类专业用户需要更 加稳定和可靠的系统。他们中大多数已经在使用现有的技术,对于是否采用H.265还心存犹豫,这就需要更长的验证周期。
另一方面,中小企业和家庭、商店用户等消费者需要低安装成本,因此更加倾向于采用新技术。基于这个原因,H.265可能首先在中小企业应用中获得成功,并在消费者市场获得认可。如果H.265标准快速成熟,其压缩效率比H.264提升50%,它就能够节省20%的投资,保证更高的性能和更替的网络和系统建设成本。
二、H.265分析工具:
Elecard HEVC Analyzer 是一款专为64位Windows系统设计的HEVC分析工具,主要用于分析编码后的.bin文件,帮助用户深入了解HEVC编码的细节和性能。通过直观的用户界面,用户可以轻松导入和分析.bin文件,获取详细的编码信息,从而优化编码策略和提升视频质量。
在 Elecard HEVC Analyzer的安装包内会有一个Elecard HEVC Analyzer v.1.x UG.pdf关于软件的使用说明。
界面如下:
上图的H.265编码结构的区域中能获取一个NALU的基本信息:
- stream区域:关于H.265流的相关信息,如色度格式、分辨率、帧率等;
- picture区域:关于I帧图片的相关信息;
- vps区域:关于VPS的相关信息;
- sps区域:关于SPS的相关信息;
- pps区域:关于PPS的相关信息;
- slice区域:关于Slice的相关信息;
- pixels区域:会显示选中的像素块的编码信息;
- predict区域:帧预测的相关信息;
上图的hex viewer十六进制数据区域中能获取到选中的视频码流的十六进制编码:
H265的一个图像序列的组成:VPS+SPS+PPS+SEI+一个I帧+若干个P帧,下面分析
十六进制数据:

处理代码如下:
java
/**
*
*
* @param data H265数据块(可能包含多个NALU)
*/
public void writeSample(byte[] data) {
int offset = 0;
while (offset < data.length) {
// 查找NALU起始码
int[] startCode = findStartCode(data, offset);
if (startCode == null) break;
int naluStart = startCode[0];
int naluEnd = findNextStartCode(data, naluStart + 1);
if (naluEnd == -1) naluEnd = data.length;
processNalu(data, naluStart, naluEnd);
offset = naluEnd;
}
}
// 辅助方法:查找起始码
private int[] findStartCode(byte[] data, int offset) {
for (int i = offset; i < data.length - 3; i++) {
if ((data[i] == 0x00 && data[i + 1] == 0x00
&& data[i + 2] == 0x00
&& data[i + 3] == 0x01)) {
return new int[]{i + 4, 4}; // 返回起始码结束位置和长度
} else if ((data[i] == 0x00
&& data[i + 1] == 0x00
&& data[i + 2] == 0x01)) {
return new int[]{i + 3, 3};
}
}
return null;
}
/**
* 查找下一个NALU起始码位置
* @param data 原始数据数组
* @param startOffset 开始搜索的位置
* @return 下一个起始码的起始位置(含起始码),未找到返回-1
*/
private int findNextStartCode(byte[] data, int startOffset) {
// 从指定偏移开始遍历(至少需要3字节空间)
for (int i = startOffset; i < data.length - 3; i++) {
// 检查4字节起始码:00 00 00 01
if (data[i] == 0x00 &&
data[i+1] == 0x00 &&
data[i+2] == 0x00 &&
data[i+3] == 0x01) {
return i; // 返回起始码的第一个字节位置
}
// 检查3字节起始码:00 00 01
if (data[i] == 0x00 &&
data[i+1] == 0x00 &&
data[i+2] == 0x01) {
return i; // 返回起始码的第一个字节位置
}
}
// 处理末尾可能的3字节情况(最后3个字节)
if (data.length - startOffset >= 3) {
int i = data.length - 3;
if (data[i] == 0x00 &&
data[i+1] == 0x00 &&
data[i+2] == 0x01) {
return i;
}
}
return -1; // 未找到下一个起始码
}
/**
* 处理单个NALU单元
*/
private void processNalu(byte[] data, int start, int end) {
int naluType = (data[start] & 0x7E) >> 1; // HEVC NALU类型
switch (naluType) {
case 32: // VPS : 00 00 01 40 --> (0x40 & 0x7E) >> 1 = 32
mVps = extractNalu(data, start, end);
break;
case 33: // SPS : 00 00 01 42 --> (0x42 & 0x7E) >> 1 = 33
mSps = extractNalu(data, start, end);
break;
case 34: // PPS : 00 00 01 44 --> (0x44 & 0x7E) >> 1 = 34
mPps = extractNalu(data, start, end);
break;
case 19: // IDR帧 : 00 00 01 26 --> (0x26 & 0x7E) >> 1 = 19
case 1: // 非关键帧
if (prepareMuxer()) {
writeFrame(data, start, end, naluType);
}
break;
}
}
// 辅助方法:提取NALU内容(去除起始码)
private byte[] extractNalu(byte[] data, int start, int end) {
byte[] nalu = new byte[end - start];
System.arraycopy(data, start, nalu, 0, nalu.length);
return nalu;
}
三、H265编码结构:
关于编码结构,可以从编码时的分层处理架构和编码完后码流的语法架构两方面进行描述。
编码时的分层处理架构:
GOP(Group Of Pictures):
视频序列由若干时间连续的图像构成,在对其进行压缩时,先将该视频序列分割为若干个小的图像组(Group Of Pictures,GOP)。在视频编码中,存在两种 GOP类型: 封闭式 GOP(Closed GOP)和开放式GOP(Open GOP)。

在封闭式GOP类型中,每一个GOP以IDR(Instantaneous Decoding Refresh)图像开始,各个GOP之间独立编解码。在开放式GOP类型中,第一个GOP中的第个帧内编码图像为IDR图像,后续GOP中的第一个内编码图像为non-IDR图像,也就是说,后面GOP 中的帧间编码图像可以越过 non-IDR图像,使用前一个GOP中的已编码图像做参考图像。每个GOP又被划分为多个片(Slice),片与片之间进行独立编解码。其主要目的之一是在数据丢失情况下进行重新同步。每个片由一个或多个片段(Slice Segment,SS)组成。
CTU(Coding Tree Unit):
H.265/HEVC 还引入了树形结构单元(Coding Tree Unit,CTU)这一概念,其类似于传统的宏块。每个CTU包括一个亮度树形编码块(Coding Tree Block,CTB)和两个色差树形编码块。一个SS在编码时,先被分割为相同大小的CTU,每一个CTU 按照四叉树分割方式被划分为不同类型的编码单元(Coding Unit,CU)。

编码完后码流的语法架构:
CVS(Coded Video Sequence):
在码流结构方面,H.265/HEVC压缩数据采用了类似于H.264/AVC的分层结构,将属于GOP层、Slice层中共用的大部分语法元素游离出来,组成序列参数集(SequenceParameterSet,SPS)和图像参数集(Picture Parameter Set, PPS)。SPS 包含了一个CVS(Coded Video Sequence)中所有图像共用的信息。其中CVS被定义为一个GOP编码后所生成的压缩数据。

SPS的内容大致包括解码相关信息,如档次级别、分辨率、某档次中编码工具开关标识和涉及的参数、时域可分级信息等。
PPS包含一幅图像所用的公共参数,即一幅图像中所有SS引用同一个PPS。其大致内容包括初始图像控制信息,如初始量化参数(Quantization Parameter,QP)、分块信息等。
此外,为了兼容标准在其他应用上的扩展,例如可分级视频编码器、多视点视频编码器,H.265/HEVC的语法架构中增加了视频参数集(Video Parameter Set,VPS)。其内容大致包括多个子层共享的语法元素,其他不属于SPS的特定信息等。对于一个SS,通过引用它所使用的PPS,该PPS又引用其对应的SPS,该SPS又引用它对应的VPS最终得到 SS的公用信息。
参数集:
参数集是一个独立的数据单位,它包含视频的不同层级编码单元的共享信息,只有当参数集直接或间接被SS引用时才有效。一个参数集并不对应某个特定的图像或CVS,同一个VPS或SPS可以被多个CVS引用,同一个PPS可以被多个图像引用。在H.265/HEVC中,NAL单元根据是否装载视频编码数据被分为VCLU(Video CodingLayer NAL Unit)和 non-VCLU。非编码数据的参数集作为non-VCLU 进行传输,这为传递关键数据提供了高鲁棒机制。参数集的独立使得其可以提前发送,也可以在需要增加新参数集的时候再发送,可以被多次重发或者采用特殊技术加以保护,甚至采用带外(Out-of-band)发送的方式。片段SS是视频编码数据的基本单位,一个SS的压缩数据生成一个 VCLU 进行传输。最终,一个视频序列的编码码流由一系列SS所生成的多个VCLU 单元和夹杂其间的一些分割标示数据和参数集数据组成。分割标示数据用于区分一个 SS 属于哪幅图像、哪个 CVS。
视频参数集(Video Parameter Set,VPS):
在 H.264/AVC的码流结构中,没有类似VPS这样的参数集去描述时域各层之间的依赖关系。它的扩展部分可伸缩视频编码中,SEI信息提供了相关各层信息,以用于不同业务和不同终端的访问。但在一些应用场景,例如广播和多播,由于SEI中的部分信息会重复出现在SPS中,这样造成参数重传而引起延迟等问题。因此在H.265/HEVC中引入VPS,以克服 H.264/AVC中存在的不足,同时为设计简洁,可扩展多层视频编码提供方便。VPS主要用于传输视频分级信息,有利于兼容标准在可分级视频编码或多视点视频编码的扩展。
一个给定的视频序列,无论它每一层的SPS是否相同,都参考相同的 VPS。VPS 包含的信息有:
- 多个子层和操作点共享的语法元素;
- 会话所需的有关操作点的关键信息,如档次、级别;
- 其他不属于SPS的操作点特性信息,例如与多层或子层相关的虚拟参考解码器(Hypothetical Reference Decoder,HRD)参数。对每个操作点的关键信息的编码,不要求可变长编码,这样有利于减轻大多数网络组成单元的负担。H.265/HEVC的扩展版本将会在当前VPS中添加更多的语法元素,以使会话更加灵活高效并使编码码率具有更高的自适应性。
序列参数集(Sequence Parameter Set ,SPS):
在 H.265/HEVC 中,一个CVS由一个随机接入点开始,第一幅图像可以是IDR图像,也可以是non-IDR图像。non-IDR图像可以是BLA(Broken Link Access)图像或CRA(Clean Random Access)图像。对于一段视频码流,其可能包含一个或者多个编码视频序列CVS。序列参数集 SPS 的内容就是包含一个CVS中所有编码图像的共享编码参数,SPS通过被PPS引用而作用于编码图像,一个CVS中所有被使用的PPS必须引用同一个SPS。实际上,SPS为所有的SS提供了公共参数,如图像的格式、档次、级等。当一个SPS被引用时,该SPS处于激活状态,直到整个CVS 结束。
SPS中所含的语法元素,其内容大致分为以下几个部分:
- 图像格式的信息。包括采样格式、图像分辨率、量化深度、解码图像是否需要裁剪输出以及相关的裁剪参数。
- 编码参数信息。包括编码块、变换块的最小尺寸和最大尺寸,帧内、帧间预测编码时变换块的最大划分深度,对4:4:4采样格式的三个通道分量是否单独编码,是否需要帧内强滤波,帧间预测过程中的某些限制条件[如非对称模式(AMP)的使用、时域MV预测的使用]是否使用量化矩阵,是否需要样点自适应补偿(SAO),是否采用PCM模式及在该模式下的相关编码参数。
- 与参考图像相关的信息。包括短期参考图像的设置,长期参考图像的使用和数目,长期参考图像的 POC 和其能否作为当前图像的参考图像。
- 档次、层和级相关参数。
- 时域分级信息。包括时域子层的最大数目,控制传输POC进位的参数,时域子层顺序标识开关,与子层相关的参数(如解码图像缓冲区的最大需求)。
- 可视化可用性信息(Video Usability Information,VUI),用于表征视频格式等额外信息。
- 其他信息。包括当前SPS引用的VPS编号、SPS标识号和SPS扩展信息。
图像参数集(Picture Parameter Set ,PPS):
在编码视频流中,一个CVS包含多幅图像,每幅图像可能包括一个或多个SS,每个SS头提供了其所引用的PPS标识号,以此得到相应PPS中的公用信息。对于同一幅图像,其内所有的SS都用同一个PPS。需要注意的是,PPS中存在一些与SPS中相同的参数,PPS中的这些参数值将会覆盖SPS中它们的取值,也就是说,SS使用PPS中的这些参数进行解码。在解码开始时,所有的PPS全部是非活动状态,而且在解码的任意时刻最多只能有一个PPS处于激活状态。当某一幅图像在其解码过程中引用了某个PPS时,这个PPS便处于激活状态,直到该图像解码结束。
PPS中所涉及的具体的语法元素,图像参数集的内容大致分为以下几个部分:
- 编码工具的可用性标志。指明片头中一些工具是否可用。这些编码工具主要包括符号位隐藏、帧内预测受限、去方块滤波、P/B 图像的加权预测、环路滤波跨越片边界或者Tile边界、Transformskip 模式和Iransquant bypass 模式。
- 量化过程相关句法元素。包括每个Slice中QP初始值的设定以及计算每个CU的QP时所需的参数。此外,还有亮度量化参数的偏移量和由它导出的色度量化参数的偏移量等。
- Tile 相关句法元素。包括 Tile 划分模式的可用性标志,以及在使用 Tile 划分模式时的一些参数,例如Tile 的划分形式,总行数、总列数及第几行、第几列的标识等。
- 去方块滤波相关句法元素。包括去方块滤波的可用性标志以及使用去方块滤波时的一些控制信息和参数,如去方块滤波的默认补偿值B和 tC。
- 片头中的控制信息。包括当前片是否为依赖片、片头中是否有额外的 Slice 头比特、图像解码顺序与输出顺序的先后关系以及CABAC中确定上下文变量初始化表格时使用的方法等。
- 其他编码一幅图像时可以共用的信息。包括ID 标识符、参考图像的数目和并行产生merge候选列表的能力等。其中D标识符用于标识当前活动的参数集,主要是当前活动的PPS的自身ID和其引用的SPS的ID。此外,PPS中还包括变换矩阵信息是否存在的标志位,这一变换矩阵信息若存在,便会对 SPS中的该信息进行覆盖。
片段层(Slice):
一幅图像可以被分割为一个或多个片(Slice),每个片的压缩数据都是独立的,Slice 头信息无法通过前一个Slice的头信息推断得到。这就要求 Slice 不能跨过它的边界来进行帧内或帧间预测,且在进行熵编码前需要进行初始化。但在进行环路滤波时,允许滤波器跨越Slice 的边界进行滤波。除了Slice 的边界可能受环路滤波影响外,Slice 的解码过程可以不使用任何来自其他Slice的影响,且有利于实现并行运算。使用Slice的主要目的是当数据丢失后能再次保证解码同步。
根据编码类型不同,Slice 可分为以下几部分:
- I Slice:该Slice 中所有CU的编码过程都使用帧内预测。
- P Slice:在ISlice 的基础上,该Slice中的CU还可以使用帧间预测,每个预测块(PB)使用至多一个运动补偿预测信息。
- B Slice:在PSlice的基础上,B Slice中的CU 也可以使用帧间预测,但是每个PB可以使用至多两个运动补偿预测信息。
一个独立的Slice可以被进一步划分为若干SS,包括一个独立SS和若干个依赖SS,并且以独立SS作为该Slice的开始。一个SS包含整数个CTU(至少一个),并且这些CTU分布在同一个NAL单元中。SS可以作为一个分组来传送视频编码数据。其中,独立SS是指它所涉及的句法元素可以由自身确定,依赖SS是指它所涉及的某些句法元素由已解码的独立SS推导得到。依赖SS可以共享独立SS携带的一些信息,例如RPS 信息、SAO的可用性和加权预测的可用性等。预测过程不能跨越独立Slice的边界,但是可以跨越依赖SS的边界,一个Slice内的SS之间可以相互参考。
如图所示,一幅图像划分为两个Slice。第一个Slice由一个包含4个CTU的独立Slicesegment、一个包含32个CTU的依赖 SS 和另外一个包含24个CTU的依赖SS组成;而第二个Slice由唯-一个包含39个CTU的独立SS组成。

H.265/HEVC 编码的最高层为 SS层,SS 层所需要的图像层信息可以通过引用相应的PPS来获得。SS头包含其引用的PPS的标识号,同一幅图像中的所有SS引用同一个PPS。此外,SS头中会存在一些与PPS中相同的参数,SS头中的这些参数值会对PPS中的该参数值进行覆盖。
Tile单元:
H.265/HEVC对H.264/AVC的改进之处还在于Tile 概念的提出。一幅图像不仅可以划分为若干个Slice,也可以划分为若干个Tile。即从水平和垂直方向将一幅图像分割为若干个矩形区域,一个矩形区域就是一个Tile。每个 Tile 包含整数个CTU,其可以独立解码。划分 Tile 的主要目的是在增强并行处理能力的同时又不引入新的错误扩散。Tile提供比CTB更大程度的并行(在图像或者子图像的层面上),在使用时无须进行复杂的线程同步。
Tile 的划分并不要求水平和垂直边界均匀分布,可根据并行计算和差错控制的要求灵活掌握。通常情况下,每一个Tie中包含的CTU 数据是近似相等的。在编码时,图像中的所有Tile 按照扫描顺序进行处理,每个Tile 中的CTU也按照扫描顺序进行编码。一个 Tile 包含的CTU个数和Slice 中的CTU个数互不影响,下图中给出了 Tile 的一种划分方式。这是3x3的划分,整幅图像被划分为9个Tile,每个Tile 都为矩形。在同一幅图像中,可以同时存在某些Slice中包含多个Tile和某些Tile中包含多个Slice 的情况。

在H.265/HEVC中,Slice和Tile 划分的目的都是为了进行独立解码但是二者的划分方式又有所不同。Tile形状基本上为矩形,Slice的形状则为条带状。Slice由一系列的SS组成,一个SS由一系列的CTU组成。
Tile 则直接由一系列的CTU组成。Slice/SS和Tile 之间必须遵守一些基本原则,每个Slice/SS 和Tile至少要满足以下两个条件之一。
- 一个 Slice/Ss 中的所有 CTU 属于同一个 Tile。
- 一个 Tile 中的所有 CTU 属于同一个 Slice/SS。
一幅图像在垂直方向上被分割为三个Tile。下图中这三个 Tile 中各自的所有CTU都属于同一个Slice。

下图中一个Slice 中的所有 CTU 都属于同一个 Tile。

树形编码块:
传统的视频编码都是基于宏块实现的,对于4:2:0采样格式的视频,一个宏块包含一个16x16大小的亮度块和两个8x8大小的色度块。考虑到高清视频/超高清视频的自身特性,H.265/HEVC 标准中引入了树形编码单元 CTU,其尺寸由编码器指定,且可大于宏块尺寸。同一位置处的一个亮度CTB和两个色度CTB,再加上相应的语法元素形成一个CTU。对于一个大小为LxL的亮度CTB,L的取值可以是16、32或64。在高分辨率视频的编码中,使用较大的CTB可以获得更好的压缩性能。
为了灵活、高效地表示视频场景中的不同纹理细节、运动变化的视频内容或者视频对象,H.265/HEVC为图像划分定义了一套全新的语法单元,包括编码单元CU、预测单元(Prediction Unit,PU)和变换单元(Transform Unit,TU)。其中编码单元是进行预测、变换、量化和熵编码等处理的基本单元,预测单元是进行帧内/帧间预测的基本单元,变换单元是进行变换和量化的基本单元。这三个单元的分离,不仅使得变换、预测和编码各个处理环节更加灵活,也使得各环节的划分更加符合视频图像的纹理特征,保证编码性能的最优化。
编码单元:
近年来标清和高清视频产业得到了蓬勃发展。大尺寸图像的一个特点是平缓区域的面积更大,用较大的块进行编码能够极大地提升编码效率。在H.264/AVC中,编码块(CodingBlock,CB)的大小是固定的。而在H.265/HEVC中,一个CTB可以直接作为一个CB,也可以进一步以四叉树的形式划分为多个小的CB。所以H.265/HEVC中CB的大小是可变的,亮度CB最大为64x64,最小为8x8。一方面大的CB可以使得平缓区域的编码效率大大提高,另一方面小的CB能很好地处理图像局部的细节,从而可以使复杂图像的预测更加准确。一个亮度CB和相应的色度CB及它们相关的句法元素共同组成一个编码单元(CU)。在H.265/HEVC中,一幅图像可以被划分为若干个互不重叠的CTU,在CTU内部,采用基于四又树的循环分层结构。同一层次上的编码单元具有相同的分割深度。一个CTU可能只包含一个CU(没有进行划分),也可能被划分为多个CU。
如下图一幅图像划分为CTU以及一个 CTU 划分为CU 的示意图:

编码单元是否继续被划分取决于分割标志位Split flag。对于编码单元 CUd,假设它的大小为 2Nx2N,深度为d。如果它对应的 Split flag 值为0,则CUa不再进行四叉树划分;反之,CUa将会被划分为4个独立的编码单元 CUd+。编码单元 CUa+的深度和大小分别变为 d+1和 NxN。
下图给出了CUd,d和Split flag 三者之间的划分关系。

这种灵活的单元表示方法与H.264/AVC中的宏块划分方法相比有以下优点。
- 编码单元的大小可以大于传统的宏块大小(16x16)。对于平坦区域,用一个较大的编码单元编码可以减少所用的比特数,提高编码效率。这一点在高清视频应用领域体现得尤为明显。
- 通过合理地选择CTU大小和最大层次深度,编码器的编码结构可以根据不同的图片内容、图片大小以及应用需求获得较大程度的优化。
- 所有的单元类型都统称为编码单元,消除了宏块与亚宏块之分,并且编码单元的结构可以根据 CTU 大小、最大编码深度以及一系列划分标志 Split flag 简单地表示出来。
预测单元:
预测单元PU规定了编码单元的所有预测模式,一切与预测有关的信息都定义在预测单元部分。比如,帧内预测的方向、帧间预测的分割方式、运动矢量预测,以及帧间预测参考图像索引号都属于预测单元的范畴。一个 2Nx2N的编码单元所包含的预测单元划分模式。
对于一个2Nx2N的CU模式,帧内预测单元PU的可选模式有两种2Nx2N和NN;帧间预测单元PU的可选模式有8种:4种对称模式(2Nx2N、2NxN、Nx2N、NxN)和4种非对称模式(2NxnU、2NxnD、nLx2N、nRx2N)。其中2NxnU和2NxnD分别以上下1:3、3:1的比率划分,nLx2N和nRx2N分别以左右1:3、3:1的比率划分。skip模式是帧间预测的一种,当需要编码的运动信息只有运动参数集索引(采用运动合并技术),编码残差信息不需要编码时,为2Nx2N skip 模式。
如图:

变换单元:
变换单元是独立完成变换和量化的基本单元,其尺寸也是灵活变化的。H.265/HEVC 突破了原有的变换尺寸限制,可支持大小为4x4~32x32的编码变换,以变换单元(TU)为基本单元进行变换和量化。它的大小依赖于CU模式,在一个CU内,允许TU跨越多个PU,以四叉树的形式递归划分。
下图给出了CTU为64x64 时各个CU 中TU的分割结构。
对于一个2Nx2N的CU,有一个标志位决定其是否划分为4个NxN的TU,是否可以进一步划分由SPS中的TU的最大划分深度决定。根据预测残差的局部变化特性,TU可以自适应地选择最优的模式。大块的TU模式能够将能量更好地集中,小块的TU模式能够保存更多的图像细节。这种灵活的分割结构,可以使变换后的残差能量得到充分压缩,以进一步提高编码增益。

档次、层和级别:
在H.264中就有对档次(Profle)和级别(level)的划分,它们规定了比特流必须要遵守的一些限制要求。而H.265HEVC在此基础上又定义了一个新的概念:层(Tier)。档次、层和级别为多种不同应用提供了兼容性。档次主要规定编码器可采用哪些编码工具或算法。级别则是指根据解码端的负载和存储空间情况对关键参数加以限制[如最大采样频率、最大图像尺寸、分辨率、最小压缩率,最大比特率和CPB(解码缓冲区)大小等]。考虑到应用可以依据最大的码率和CPB大小来区分,因此有些 Level 定义了两个 Tier:主层(Main Tier)和高层(High Tier)主层用于大多数应用,高层用于那些最苛刻的应用。满足某些Level或Tier 的解码器应当可以解码当前 Level和 Tier,以及比当前 Level 和 Tier更低的 Level和Tier 的所有码流,满足某一Profile的解码器必须支持该Profile中的所有特性。编码器不必实现Profle中的所有特性,但生成的码流必须遵守标准规定。
档次:
在H.265/HEVC标准中提出了三种档次,分别是Main,Main10和Main Still Picture。这三个档次的限制条件如下:
- 只支持4:2:0色度采样信号;
- 使用了 Tiles 便不能使用 WPP,每一个 Tile 的亮度分辨率至少要为 256x64;
- Main和 Main Stil Picture档次支持8位像素深度,Main 10档次则支持10位像素深度,MainStillPicture档次不支持帧间预测。
main:
支持每像素8比特的位深、4:2:0的采样格式,是最常见的档次。
main 10:
2012年10月的会议上,提案JCTVC-K0109提出了10比特位深的档次,其指出10比特位深的图像有助于提高视频质量。该提案获得通过,这一技术主要应用于消费电子领域。Main10档次支持每像素8比特或者10比特的位深、4:2:0的采样格式。由于采用更多的比特来描述像素值,Main 10可以大幅度提高重构视频的质量。支持Main10档次的解码器必须同时可以解码 Main和Main 10档次的码流。
Main Still Picture:
Main Still Picture档次支持单个静止图像,其按照Main档次的规定进行编码。为了测试MainStill Picture 档次下静态图像的压缩性能,将H.265/HEVC HM 8.0rc2,JPEG 2000 kakadu v6.0 和 JPEG IJG v6b 进行实验对比。视频质量评价标准采用基于PSNR的客观评价和基于平均意见得分(MOS)的主观评价。对于4:2:0色度采样信号,相比于JPEG2000和JPEG,在相同重构视频质量下(PSNR度量),H.265/HEVC编码下得到的码率分别下降了20.26%和61.63%;在相同重构视频质量下(MOS度量),H.265/HEVC编码下得到的码率分别下降了30.96%和43.10%。
层和级别:
H.265/HEVC标准中定义了两个层和13个级,两个层分别是 Main Tier 和 High Tier。4和4以上的8个Level 支持 High Tier。 Tier按其最高比特率来处理应用问题,Main Tier可适用于大多数应用,HighTier 用于高需求应用。符合某一 Tier/Level的解码器能够解码当前以及比当前 Tier/Level 低的所有码流。
同一个Level实际上就是一套对编码比特流的一系列编码参数的限制。H.265/HEVC的13个级支持从OCIF到8k多种分辨率的图像。图像宽高受到该级别定义参数MaxLumaPS的限制-图像的宽和高均须小于等于8倍的MaxLumaPS再开方。此外,Level还约束了每幅图像中垂直和水平方向 Tile 的最大数量,以及每秒最大的 Tile 数量。
四、代码解析H.265/Hevc:
根据文章中**Rec. ITU-T H.265 (V10) (07/2024)**的文档描述H.265/Hevc进行如下代码设计:
代码设计总概:
- 第一步:找到以0x00 00 01或0x00 00 00 01为分割分离出Nalu的切片单元,存入一个vector集合中;
- 第二步:设计H265NalUnitParser类包含nal_unit_header和nal_unit_payload的结构体,并抽象出解析Nalu的ParseNalUnit方法;
- 第三步:根据Rec. ITU-T H.265文档中的7.3.1.1和7.3.1.2解析出NalUnit Header;
- 第四步:根据第二步分离出来的NalUnit Header数据中nal_unit_type,从nal_unit_payload划分出VPS/SPS/cSlice;
- 第五步:解析出VPS参数集的所有属性;
- 第六步:解析出SPS参数集的所有属性;
- 第七步:解析出PPS参数集的所有属性;
- 其他:按照Rec. ITU-T H.265文档解析出其余参数集;
1.分割分离出Nalu的切片单元:
以0x00 00 01或0x00 00 00 01为分割
cpp
std::vector<H265BitstreamParser::NaluIndex>
H265BitstreamParser::FindNaluIndices(const uint8_t* data,
size_t length) noexcept {
// This is sorta like Boyer-Moore, but with only the first optimization step:
// given a 3-byte sequence we're looking at, if the 3rd byte isn't 1 or 0,
// skip ahead to the next 3-byte sequence. 0s and 1s are relatively rare, so
// this will skip the majority of reads/checks.
std::vector<NaluIndex> sequences;
if (length < kNaluShortStartSequenceSize) {
return sequences;
}
const size_t end = length - kNaluShortStartSequenceSize;
for (size_t i = 0; i < end;) {
if (data[i + 2] > 1) {
i += 3;
} else if (data[i + 2] == 0x01 && data[i + 1] == 0x00 && data[i] == 0x00) {
// We found a start sequence, now check if it was a 3 of 4 byte one.
NaluIndex index = {i, i + 3, 0};
if (index.start_offset > 0 && data[index.start_offset - 1] == 0)
--index.start_offset;
// Update length of previous entry.
auto it = sequences.rbegin();
if (it != sequences.rend())
it->payload_size = index.start_offset - it->payload_start_offset;
sequences.push_back(index);
i += 3;
} else {
++i;
}
}
// Update length of last entry, if any.
auto it = sequences.rbegin();
if (it != sequences.rend())
it->payload_size = length - it->payload_start_offset;
return sequences;
}
2.H265NalUnitParser类:
设计H265NalUnitParser类包含nal_unit_header和nal_unit_payload的结构体,并抽象出解析Nalu的ParseNalUnit方法;
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#pragma once
#include <stdio.h>
#include <memory>
#include "h265_common.h"
#include "h265_nal_unit_header_parser.h"
#include "h265_nal_unit_payload_parser.h"
#include "bit_buffer.h"
namespace h265nal {
// A class for parsing out an H265 NAL Unit.
class H265NalUnitParser {
public:
// The parsed state of the NAL Unit. Only some select values are stored.
// Add more as they are actually needed.
struct NalUnitState {
NalUnitState() = default;
~NalUnitState() = default;
// disable copy ctor, move ctor, and copy&move assignments
NalUnitState(const NalUnitState&) = delete;
NalUnitState(NalUnitState&&) = delete;
NalUnitState& operator=(const NalUnitState&) = delete;
NalUnitState& operator=(NalUnitState&&) = delete;
#ifdef FDUMP_DEFINE
void fdump(FILE* outfp, int indent_level, bool add_offset, bool add_length,
bool add_parsed_length, bool add_checksum) const;
#endif // FDUMP_DEFINE
// NAL Unit offset in the full blob
size_t offset;
// NAL Unit length
size_t length;
// NAL Unit parsed length
size_t parsed_length;
// NAL Unit checksum
std::shared_ptr<NaluChecksum> checksum;
std::unique_ptr<struct H265NalUnitHeaderParser::NalUnitHeaderState>
nal_unit_header;
std::unique_ptr<struct H265NalUnitPayloadParser::NalUnitPayloadState>
nal_unit_payload;
};
// Unpack RBSP and parse NAL unit state from the supplied buffer.
static std::unique_ptr<NalUnitState> ParseNalUnit(
const uint8_t* data, size_t length,
struct H265BitstreamParserState* bitstream_parser_state,
bool add_checksum) noexcept;
static std::unique_ptr<NalUnitState> ParseNalUnit(
rtc::BitBuffer* bit_buffer,
struct H265BitstreamParserState* bitstream_parser_state,
bool add_checksum) noexcept;
static std::unique_ptr<NalUnitState> ParseNalUnit(
const uint8_t* data, size_t length,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
return ParseNalUnit(data, length, bitstream_parser_state,
/*add_checksum*/ false);
}
static std::unique_ptr<NalUnitState> ParseNalUnit(
rtc::BitBuffer* bit_buffer,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
return ParseNalUnit(bit_buffer, bitstream_parser_state,
/*add_checksum*/ false);
}
};
} // namespace h265nal
3. 解析出NalUnit Header:
根据Rec. ITU-T H.265文档中的7.3.1.1和7.3.1.2解析出NalUnit Header;
参数集

根据上参数集表,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_nal_unit_header_parser.h"
#include <stdio.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse NAL Unit header state from the supplied buffer.
std::unique_ptr<H265NalUnitHeaderParser::NalUnitHeaderState>
H265NalUnitHeaderParser::ParseNalUnitHeader(const uint8_t* data,
size_t length) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseNalUnitHeader(&bit_buffer);
}
bool H265NalUnitHeaderParser::GetNalUnitType(const uint8_t* data,
const size_t length,
NalUnitType& naluType) noexcept {
rtc::BitBuffer bitBuffer(data, length);
auto naluHeader = ParseNalUnitHeader(&bitBuffer);
if (!naluHeader) {
return false;
}
naluType = static_cast<NalUnitType>(naluHeader->nal_unit_type);
return true;
}
std::unique_ptr<H265NalUnitHeaderParser::NalUnitHeaderState>
H265NalUnitHeaderParser::ParseNalUnitHeader(
rtc::BitBuffer* bit_buffer) noexcept {
// H265 NAL Unit Header (nal_unit_header()) parser.
// Section 7.3.1.2 ("NAL unit header syntax") of the H.265
// standard for a complete description.
auto nal_unit_header = std::make_unique<NalUnitHeaderState>();
// forbidden_zero_bit f(1)
if (!bit_buffer->ReadBits(&nal_unit_header->forbidden_zero_bit, 1)) {
return nullptr;
}
// nal_unit_type u(6)
if (!bit_buffer->ReadBits(&nal_unit_header->nal_unit_type, 6)) {
return nullptr;
}
// nuh_layer_id u(6)
if (!bit_buffer->ReadBits(&nal_unit_header->nuh_layer_id, 6)) {
return nullptr;
}
// nuh_temporal_id_plus1 u(3)
if (!bit_buffer->ReadBits(&nal_unit_header->nuh_temporal_id_plus1, 3)) {
return nullptr;
}
return nal_unit_header;
}
#ifdef FDUMP_DEFINE
void H265NalUnitHeaderParser::NalUnitHeaderState::fdump(
FILE* outfp, int indent_level) const {
fprintf(outfp, "nal_unit_header {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "forbidden_zero_bit: %i", forbidden_zero_bit);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "nal_unit_type: %i", nal_unit_type);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "nuh_layer_id: %i", nuh_layer_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "nuh_temporal_id_plus1: %i", nuh_temporal_id_plus1);
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
4.划分出VPS/SPS/cSlice:
根据第二步分离出来的NalUnit Header数据中nal_unit_type,从nal_unit_payload划分出VPS/SPS/Slice;
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_nal_unit_payload_parser.h"
#include <stdio.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_aud_parser.h"
#include "../h265include/h265nal/h265_common.h"
#include "../h265include/h265nal/h265_pps_parser.h"
#include "../h265include/h265nal/h265_slice_parser.h"
#include "../h265include/h265nal/h265_sps_parser.h"
#include "../h265include/h265nal/h265_vps_parser.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse NAL Unit payload state from the supplied buffer.
std::unique_ptr<H265NalUnitPayloadParser::NalUnitPayloadState>
H265NalUnitPayloadParser::ParseNalUnitPayload(
const uint8_t* data, size_t length, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseNalUnitPayload(&bit_buffer, nal_unit_type,
bitstream_parser_state);
}
std::unique_ptr<H265NalUnitPayloadParser::NalUnitPayloadState>
H265NalUnitPayloadParser::ParseNalUnitPayload(
rtc::BitBuffer* bit_buffer, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
// H265 NAL Unit Payload (nal_unit()) parser.
// Section 7.3.1.1 ("General NAL unit header syntax") of the H.265
// standard for a complete description.
auto nal_unit_payload = std::make_unique<NalUnitPayloadState>();
// payload (Table 7-1, Section 7.4.2.2)
switch (nal_unit_type) {
case TRAIL_N:
case TRAIL_R:
case TSA_N:
case TSA_R:
case STSA_N:
case STSA_R:
case RADL_N:
case RADL_R:
case RASL_N:
case RASL_R: {
// slice_segment_layer_rbsp()
nal_unit_payload->slice_segment_layer =
H265SliceSegmentLayerParser::ParseSliceSegmentLayer(
bit_buffer, nal_unit_type, bitstream_parser_state);
break;
}
case RSV_VCL_N10:
case RSV_VCL_R11:
case RSV_VCL_N12:
case RSV_VCL_R13:
case RSV_VCL_N14:
case RSV_VCL_R15:
// reserved, non-IRAP, sub-layer non-reference pictures
break;
case BLA_W_LP:
case BLA_W_RADL:
case BLA_N_LP:
case IDR_W_RADL:
case IDR_N_LP:
case CRA_NUT: {
// slice_segment_layer_rbsp()
nal_unit_payload->slice_segment_layer =
H265SliceSegmentLayerParser::ParseSliceSegmentLayer(
bit_buffer, nal_unit_type, bitstream_parser_state);
break;
}
case RSV_IRAP_VCL22:
case RSV_IRAP_VCL23:
// reserved, IRAP pictures
break;
case RSV_VCL24:
case RSV_VCL25:
case RSV_VCL26:
case RSV_VCL27:
case RSV_VCL28:
case RSV_VCL29:
case RSV_VCL30:
case RSV_VCL31:
// reserved, non-IRAP pictures
break;
case VPS_NUT: {
// video_parameter_set_rbsp()
nal_unit_payload->vps = H265VpsParser::ParseVps(bit_buffer);
if (nal_unit_payload->vps != nullptr) {
uint32_t vps_id = nal_unit_payload->vps->vps_video_parameter_set_id;
bitstream_parser_state->vps[vps_id] = nal_unit_payload->vps;
}
break;
}
case SPS_NUT: {
// seq_parameter_set_rbsp()
nal_unit_payload->sps = H265SpsParser::ParseSps(bit_buffer);
if (nal_unit_payload->sps != nullptr) {
uint32_t sps_id = nal_unit_payload->sps->sps_seq_parameter_set_id;
bitstream_parser_state->sps[sps_id] = nal_unit_payload->sps;
}
break;
}
case PPS_NUT: {
// pic_parameter_set_rbsp()
nal_unit_payload->pps = H265PpsParser::ParsePps(bit_buffer);
if (nal_unit_payload->pps != nullptr) {
uint32_t pps_id = nal_unit_payload->pps->pps_pic_parameter_set_id;
bitstream_parser_state->pps[pps_id] = nal_unit_payload->pps;
}
break;
}
case AUD_NUT: {
// access_unit_delimiter_rbsp()
nal_unit_payload->aud = H265AudParser::ParseAud(bit_buffer);
break;
}
case EOS_NUT:
// end_of_seq_rbsp()
// TODO(chemag): add support for end_of_seq(()
break;
case EOB_NUT:
// end_of_bitstream_rbsp()
// TODO(chemag): add support for end_of_bitstream(()
break;
case FD_NUT:
// filler_data_rbsp()
// TODO(chemag): add support for filler_data()
break;
case PREFIX_SEI_NUT:
// sei_rbsp()
// TODO(chemag): add support for supplemental_enhancement_information()
// (sei)
break;
case SUFFIX_SEI_NUT:
// TODO(chemag): add support for supplemental_enhancement_information()
// (sei) sei_rbsp()
break;
case RSV_NVCL41:
case RSV_NVCL42:
case RSV_NVCL43:
case RSV_NVCL44:
case RSV_NVCL45:
case RSV_NVCL46:
case RSV_NVCL47:
// reserved
break;
default:
// unspecified
break;
}
return nal_unit_payload;
}
#ifdef FDUMP_DEFINE
void H265NalUnitPayloadParser::NalUnitPayloadState::fdump(
FILE* outfp, int indent_level, uint32_t nal_unit_type) const {
fprintf(outfp, "nal_unit_payload {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
switch (nal_unit_type) {
case TRAIL_N:
case TRAIL_R:
case TSA_N:
case TSA_R:
case STSA_N:
case STSA_R:
case RADL_N:
case RADL_R:
case RASL_N:
case RASL_R:
if (slice_segment_layer) {
slice_segment_layer->fdump(outfp, indent_level);
}
break;
case RSV_VCL_N10:
case RSV_VCL_R11:
case RSV_VCL_N12:
case RSV_VCL_R13:
case RSV_VCL_N14:
case RSV_VCL_R15:
// reserved, non-IRAP, sub-layer non-reference pictures
break;
case BLA_W_LP:
case BLA_W_RADL:
case BLA_N_LP:
case IDR_W_RADL:
case IDR_N_LP:
case CRA_NUT:
if (slice_segment_layer) {
slice_segment_layer->fdump(outfp, indent_level);
}
break;
case RSV_IRAP_VCL22:
case RSV_IRAP_VCL23:
// reserved, IRAP pictures
break;
case RSV_VCL24:
case RSV_VCL25:
case RSV_VCL26:
case RSV_VCL27:
case RSV_VCL28:
case RSV_VCL29:
case RSV_VCL30:
case RSV_VCL31:
// reserved, non-IRAP pictures
break;
case VPS_NUT:
if (vps) {
vps->fdump(outfp, indent_level);
}
break;
case SPS_NUT:
if (sps) {
sps->fdump(outfp, indent_level);
}
break;
case PPS_NUT:
if (pps) {
pps->fdump(outfp, indent_level);
}
break;
case AUD_NUT:
aud->fdump(outfp, indent_level);
break;
case EOS_NUT:
// end_of_seq_rbsp()
// TODO(chemag): add support for end_of_seq(()
break;
case EOB_NUT:
// end_of_bitstream_rbsp()
// TODO(chemag): add support for end_of_bitstream(()
break;
case FD_NUT:
// filler_data_rbsp()
// TODO(chemag): add support for filler_data()
break;
case PREFIX_SEI_NUT:
// sei_rbsp()
// TODO(chemag): add support for supplemental_enhancement_information()
// (sei)
break;
case SUFFIX_SEI_NUT:
// sei_rbsp()
// TODO(chemag): add support for supplemental_enhancement_information()
// (sei)
break;
case RSV_NVCL41:
case RSV_NVCL42:
case RSV_NVCL43:
case RSV_NVCL44:
case RSV_NVCL45:
case RSV_NVCL46:
case RSV_NVCL47:
// reserved
break;
default:
// unspecified
break;
}
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
5.解析VPS参数集:
根据Rec. ITU-T H.265文档中的7.3.2.1中的VPS参数集:
VPS参数总集:


根据上参数总集表,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_vps_parser.h"
#include <stdio.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
#include "../h265include/h265nal/h265_hrd_parameters_parser.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse VPS state from the supplied buffer.
std::shared_ptr<H265VpsParser::VpsState> H265VpsParser::ParseVps(
const uint8_t* data, size_t length) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseVps(&bit_buffer);
}
std::shared_ptr<H265VpsParser::VpsState> H265VpsParser::ParseVps(
rtc::BitBuffer* bit_buffer) noexcept {
uint32_t bits_tmp;
uint32_t golomb_tmp;
// H265 VPS (video_parameter_set_rbsp()) NAL Unit.
// Section 7.3.2.1 ("Video parameter set data syntax") of the H.265
// standard for a complete description.
auto vps = std::make_shared<VpsState>();
// vps_video_parameter_set_id u(4)
if (!bit_buffer->ReadBits(&(vps->vps_video_parameter_set_id), 4)) {
return nullptr;
}
// vps_base_layer_internal_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_base_layer_internal_flag), 1)) {
return nullptr;
}
// vps_base_layer_available_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_base_layer_available_flag), 1)) {
return nullptr;
}
// vps_max_layers_minus1 u(6)
if (!bit_buffer->ReadBits(&(vps->vps_max_layers_minus1), 6)) {
return nullptr;
}
// vps_max_sub_layers_minus1 u(3)
if (!bit_buffer->ReadBits(&(vps->vps_max_sub_layers_minus1), 3)) {
return nullptr;
}
// vps_temporal_id_nesting_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_temporal_id_nesting_flag), 1)) {
return nullptr;
}
// vps_reserved_0xffff_16bits u(16)
if (!bit_buffer->ReadBits(&(vps->vps_reserved_0xffff_16bits), 16)) {
return nullptr;
}
// profile_tier_level(1, vps_max_sub_layers_minus1)
vps->profile_tier_level = H265ProfileTierLevelParser::ParseProfileTierLevel(
bit_buffer, true, vps->vps_max_sub_layers_minus1);
if (vps->profile_tier_level == nullptr) {
return nullptr;
}
// vps_sub_layer_ordering_info_present_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_sub_layer_ordering_info_present_flag),
1)) {
return nullptr;
}
for (uint32_t i = (vps->vps_sub_layer_ordering_info_present_flag
? 0
: vps->vps_max_sub_layers_minus1);
i <= vps->vps_max_sub_layers_minus1; i++) {
// vps_max_dec_pic_buffering_minus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
vps->vps_max_dec_pic_buffering_minus1.push_back(golomb_tmp);
// vps_max_num_reorder_pics[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
vps->vps_max_num_reorder_pics.push_back(golomb_tmp);
// vps_max_latency_increase_plus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
vps->vps_max_latency_increase_plus1.push_back(golomb_tmp);
}
// vps_max_layer_id u(6)
if (!bit_buffer->ReadBits(&(vps->vps_max_layer_id), 6)) {
return nullptr;
}
if (vps->vps_max_layer_id > h265limits::VPS_MAX_LAYER_ID_MAX) {
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: vps->vps_max_layer_id value too large: %i\n",
vps->vps_max_layer_id);
#endif // FPRINT_ERRORS
return nullptr;
}
// vps_num_layer_sets_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(vps->vps_num_layer_sets_minus1))) {
return nullptr;
}
if (vps->vps_num_layer_sets_minus1 >
h265limits::VPS_NUM_LAYER_SETS_MINUS1_MAX) {
#ifdef FPRINT_ERRORS
fprintf(stderr,
"error: vps->vps_num_layer_sets_minus1 value too large: %i\n",
vps->vps_num_layer_sets_minus1);
#endif // FPRINT_ERRORS
return nullptr;
}
for (uint32_t i = 1; i <= vps->vps_num_layer_sets_minus1; i++) {
vps->layer_id_included_flag.emplace_back();
for (uint32_t j = 0; j <= vps->vps_max_layer_id; j++) {
// layer_id_included_flag[i][j] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
vps->layer_id_included_flag[i - 1].push_back(bits_tmp);
}
}
// vps_timing_info_present_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_timing_info_present_flag), 1)) {
return nullptr;
}
if (vps->vps_timing_info_present_flag) {
// vps_num_units_in_tick u(32)
if (!bit_buffer->ReadBits(&(vps->vps_num_units_in_tick), 32)) {
return nullptr;
}
// vps_time_scale u(32)
if (!bit_buffer->ReadBits(&(vps->vps_time_scale), 32)) {
return nullptr;
}
// vps_poc_proportional_to_timing_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_poc_proportional_to_timing_flag), 1)) {
return nullptr;
}
if (vps->vps_poc_proportional_to_timing_flag) {
// vps_num_ticks_poc_diff_one_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(vps->vps_num_ticks_poc_diff_one_minus1))) {
return nullptr;
}
}
// vps_num_hrd_parameters ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(vps->vps_num_hrd_parameters))) {
return nullptr;
}
for (uint32_t i = 0; i < vps->vps_num_hrd_parameters; i++) {
// hrd_layer_set_idx[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
vps->hrd_layer_set_idx.push_back(golomb_tmp);
if (i > 0) {
// cprms_present_flag[i] u(1)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
vps->cprms_present_flag.push_back(golomb_tmp);
} else {
vps->cprms_present_flag.push_back(0);
}
// hrd_parameters(cprms_present_flag[i], vps_max_sub_layers_minus1)
vps->hrd_parameters = H265HrdParametersParser::ParseHrdParameters(
bit_buffer, vps->cprms_present_flag[i],
vps->vps_max_sub_layers_minus1);
if (vps->hrd_parameters == nullptr) {
return nullptr;
}
}
}
// vps_extension_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_extension_flag), 1)) {
return nullptr;
}
if (vps->vps_extension_flag) {
while (more_rbsp_data(bit_buffer)) {
// vps_extension_data_flag u(1)
if (!bit_buffer->ReadBits(&(vps->vps_extension_data_flag), 1)) {
return nullptr;
}
}
}
rbsp_trailing_bits(bit_buffer);
return vps;
}
#ifdef FDUMP_DEFINE
void H265VpsParser::VpsState::fdump(FILE* outfp, int indent_level) const {
fprintf(outfp, "vps {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_video_parameter_set_id: %i", vps_video_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_base_layer_internal_flag: %i",
vps_base_layer_internal_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_base_layer_available_flag: %i",
vps_base_layer_available_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_layers_minus1: %i", vps_max_layers_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_sub_layers_minus1: %i", vps_max_sub_layers_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_temporal_id_nesting_flag: %i",
vps_temporal_id_nesting_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_reserved_0xffff_16bits: 0x%04x",
vps_reserved_0xffff_16bits);
fdump_indent_level(outfp, indent_level);
profile_tier_level->fdump(outfp, indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_sub_layer_ordering_info_present_flag: %i",
vps_sub_layer_ordering_info_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_dec_pic_buffering_minus1 {");
for (const uint32_t& v : vps_max_dec_pic_buffering_minus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_num_reorder_pics {");
for (const uint32_t& v : vps_max_num_reorder_pics) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_latency_increase_plus1 {");
for (const uint32_t& v : vps_max_latency_increase_plus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_max_layer_id: %i", vps_max_layer_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_num_layer_sets_minus1: %i", vps_num_layer_sets_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "layer_id_included_flag {");
for (const std::vector<uint32_t>& vv : layer_id_included_flag) {
fprintf(outfp, " {");
for (const uint32_t& v : vv) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_timing_info_present_flag: %i",
vps_timing_info_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_num_units_in_tick: %i", vps_num_units_in_tick);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_time_scale: %i", vps_time_scale);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_poc_proportional_to_timing_flag: %i",
vps_poc_proportional_to_timing_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_num_ticks_poc_diff_one_minus1: %i",
vps_num_ticks_poc_diff_one_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_num_hrd_parameters: %i", vps_num_hrd_parameters);
if (vps_num_hrd_parameters > 0) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "hrd_layer_set_idx {");
for (const uint32_t& v : hrd_layer_set_idx) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cprms_present_flag {");
for (const uint32_t& v : cprms_present_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
// hrd_parameters(cprms_present_flag[i], vps_max_sub_layers_minus1)
fdump_indent_level(outfp, indent_level);
hrd_parameters->fdump(outfp, indent_level);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_extension_flag: %i", vps_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vps_extension_data_flag: %i", vps_extension_data_flag);
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
ProfileTierLevel子集:
在文档7.3.2.2中的profile_tier_level( 1, sps_max_sub_layers_minus1 )可跳转到7.3.3的ProfileTierLevel参数集。




根据上ProfileTierLevel子集,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_profile_tier_level_parser.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdio.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse PROFILE_TIER_LEVEL state from the supplied buffer.
std::unique_ptr<H265ProfileTierLevelParser::ProfileTierLevelState>
H265ProfileTierLevelParser::ParseProfileTierLevel(
const uint8_t* data, size_t length, const bool profilePresentFlag,
const unsigned int maxNumSubLayersMinus1) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseProfileTierLevel(&bit_buffer, profilePresentFlag,
maxNumSubLayersMinus1);
}
std::unique_ptr<H265ProfileTierLevelParser::ProfileTierLevelState>
H265ProfileTierLevelParser::ParseProfileTierLevel(
rtc::BitBuffer* bit_buffer, const bool profilePresentFlag,
const unsigned int maxNumSubLayersMinus1) noexcept {
uint32_t bits_tmp;
// profile_tier_level() parser.
// Section 7.3.3 ("Profile, tier and level syntax") of the H.265
// standard for a complete description.
auto profile_tier_level = std::make_unique<ProfileTierLevelState>();
// input parameters
profile_tier_level->profilePresentFlag = profilePresentFlag;
profile_tier_level->maxNumSubLayersMinus1 = maxNumSubLayersMinus1;
if (profilePresentFlag) {
profile_tier_level->general =
H265ProfileInfoParser::ParseProfileInfo(bit_buffer);
if (profile_tier_level->general == nullptr) {
return nullptr;
}
}
// general_level_idc u(8)
if (!bit_buffer->ReadBits(&(profile_tier_level->general_level_idc), 8)) {
return nullptr;
}
for (uint32_t i = 0; i < maxNumSubLayersMinus1; i++) {
// sub_layer_profile_present_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
profile_tier_level->sub_layer_profile_present_flag.push_back(bits_tmp);
// sub_layer_level_present_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
profile_tier_level->sub_layer_level_present_flag.push_back(bits_tmp);
}
if (maxNumSubLayersMinus1 > 0) {
for (uint32_t i = maxNumSubLayersMinus1; i < 8; i++) {
// reserved_zero_2bits[i] u(2)
if (!bit_buffer->ReadBits(&bits_tmp, 2)) {
return nullptr;
}
profile_tier_level->reserved_zero_2bits.push_back(bits_tmp);
}
}
for (uint32_t i = 0; i < maxNumSubLayersMinus1; i++) {
if (profile_tier_level->sub_layer_profile_present_flag[i]) {
profile_tier_level->sub_layer.push_back(
H265ProfileInfoParser::ParseProfileInfo(bit_buffer));
if (profile_tier_level->sub_layer.back() == nullptr) {
return nullptr;
}
}
if (profile_tier_level->sub_layer_profile_present_flag[i]) {
// sub_layer_level_idc u(8)
if (!bit_buffer->ReadBits(&bits_tmp, 8)) {
return nullptr;
}
profile_tier_level->sub_layer_level_idc.push_back(bits_tmp);
}
}
return profile_tier_level;
}
std::unique_ptr<H265ProfileInfoParser::ProfileInfoState>
H265ProfileInfoParser::ParseProfileInfo(const uint8_t* data,
size_t length) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseProfileInfo(&bit_buffer);
}
std::unique_ptr<H265ProfileInfoParser::ProfileInfoState>
H265ProfileInfoParser::ParseProfileInfo(rtc::BitBuffer* bit_buffer) noexcept {
auto profile_info = std::make_unique<ProfileInfoState>();
uint32_t bits_tmp, bits_tmp_hi;
// profile_space u(2)
if (!bit_buffer->ReadBits(&profile_info->profile_space, 2)) {
return nullptr;
}
// tier_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->tier_flag, 1)) {
return nullptr;
}
// profile_idc u(5)
if (!bit_buffer->ReadBits(&profile_info->profile_idc, 5)) {
return nullptr;
}
// for (j = 0; j < 32; j++)
for (uint32_t j = 0; j < 32; j++) {
// profile_compatibility_flag[j] u(1)
if (!bit_buffer->ReadBits(&profile_info->profile_compatibility_flag[j],
1)) {
return nullptr;
}
}
// progressive_source_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->progressive_source_flag, 1)) {
return nullptr;
}
// interlaced_source_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->interlaced_source_flag, 1)) {
return nullptr;
}
// non_packed_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->non_packed_constraint_flag, 1)) {
return nullptr;
}
// frame_only_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->frame_only_constraint_flag, 1)) {
return nullptr;
}
if (profile_info->profile_idc == 4 ||
profile_info->profile_compatibility_flag[4] == 1 ||
profile_info->profile_idc == 5 ||
profile_info->profile_compatibility_flag[5] == 1 ||
profile_info->profile_idc == 6 ||
profile_info->profile_compatibility_flag[6] == 1 ||
profile_info->profile_idc == 7 ||
profile_info->profile_compatibility_flag[7] == 1 ||
profile_info->profile_idc == 8 ||
profile_info->profile_compatibility_flag[8] == 1 ||
profile_info->profile_idc == 9 ||
profile_info->profile_compatibility_flag[9] == 1 ||
profile_info->profile_idc == 10 ||
profile_info->profile_compatibility_flag[10] == 1) {
// The number of bits in this syntax structure is not affected by
// this condition
// max_12bit_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_12bit_constraint_flag, 1)) {
return nullptr;
}
// max_10bit_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_10bit_constraint_flag, 1)) {
return nullptr;
}
// max_8bit_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_8bit_constraint_flag, 1)) {
return nullptr;
}
// max_422chroma_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_422chroma_constraint_flag,
1)) {
return nullptr;
}
// max_420chroma_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_420chroma_constraint_flag,
1)) {
return nullptr;
}
// max_monochrome_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_monochrome_constraint_flag,
1)) {
return nullptr;
}
// intra_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->intra_constraint_flag, 1)) {
return nullptr;
}
// one_picture_only_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->one_picture_only_constraint_flag,
1)) {
return nullptr;
}
// lower_bit_rate_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->lower_bit_rate_constraint_flag,
1)) {
return nullptr;
}
if (profile_info->profile_idc == 5 ||
profile_info->profile_compatibility_flag[5] == 1 ||
profile_info->profile_idc == 9 ||
profile_info->profile_compatibility_flag[9] == 1 ||
profile_info->profile_idc == 10 ||
profile_info->profile_compatibility_flag[10] == 1) {
// max_14bit_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->max_14bit_constraint_flag, 1)) {
return nullptr;
}
// reserved_zero_33bits u(33)
if (!bit_buffer->ReadBits(&bits_tmp_hi, 1)) {
return nullptr;
}
if (!bit_buffer->ReadBits(&bits_tmp, 32)) {
return nullptr;
}
profile_info->reserved_zero_33bits =
((uint64_t)bits_tmp_hi << 32) | bits_tmp;
} else {
// reserved_zero_34bits u(34)
if (!bit_buffer->ReadBits(&bits_tmp_hi, 2)) {
return nullptr;
}
if (!bit_buffer->ReadBits(&bits_tmp, 32)) {
return nullptr;
}
profile_info->reserved_zero_34bits =
((uint64_t)bits_tmp_hi << 32) | bits_tmp;
}
} else if (profile_info->profile_idc == 2 ||
profile_info->profile_compatibility_flag[2] == 1) {
// reserved_zero_7bits u(7)
if (!bit_buffer->ReadBits(&profile_info->reserved_zero_7bits, 7)) {
return nullptr;
}
// one_picture_only_constraint_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->one_picture_only_constraint_flag,
1)) {
return nullptr;
}
// reserved_zero_35bits u(35)
if (!bit_buffer->ReadBits(&bits_tmp_hi, 3)) {
return nullptr;
}
if (!bit_buffer->ReadBits(&bits_tmp, 32)) {
return nullptr;
}
profile_info->reserved_zero_35bits =
((uint64_t)bits_tmp_hi << 32) | bits_tmp;
} else {
// reserved_zero_43bits u(43)
if (!bit_buffer->ReadBits(&bits_tmp_hi, 11)) {
return nullptr;
}
if (!bit_buffer->ReadBits(&bits_tmp, 32)) {
return nullptr;
}
profile_info->reserved_zero_43bits =
((uint64_t)bits_tmp_hi << 32) | bits_tmp;
}
// The number of bits in this syntax structure is not affected by
// this condition
if ((profile_info->profile_idc >= 1 && profile_info->profile_idc <= 5) ||
profile_info->profile_idc == 9 ||
profile_info->profile_compatibility_flag[1] == 1 ||
profile_info->profile_compatibility_flag[2] == 1 ||
profile_info->profile_compatibility_flag[3] == 1 ||
profile_info->profile_compatibility_flag[4] == 1 ||
profile_info->profile_compatibility_flag[5] == 1 ||
profile_info->profile_compatibility_flag[9] == 1) {
// inbld_flag u(1)
if (!bit_buffer->ReadBits(&profile_info->inbld_flag, 1)) {
return nullptr;
}
} else {
// reserved_zero_bit u(1)
if (!bit_buffer->ReadBits(&profile_info->reserved_zero_bit, 1)) {
return nullptr;
}
}
return profile_info;
}
#ifdef FDUMP_DEFINE
void H265ProfileInfoParser::ProfileInfoState::fdump(FILE* outfp,
int indent_level) const {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "profile_space: %i", profile_space);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "tier_flag: %i", tier_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "profile_idc: %i", profile_idc);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "profile_compatibility_flag {");
for (const uint32_t& v : profile_compatibility_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "progressive_source_flag: %i", progressive_source_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "interlaced_source_flag: %i", interlaced_source_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "non_packed_constraint_flag: %i", non_packed_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "frame_only_constraint_flag: %i", frame_only_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_12bit_constraint_flag: %i", max_12bit_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_10bit_constraint_flag: %i", max_10bit_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_8bit_constraint_flag: %i", max_8bit_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_422chroma_constraint_flag: %i",
max_422chroma_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_420chroma_constraint_flag: %i",
max_420chroma_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_monochrome_constraint_flag: %i",
max_monochrome_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "intra_constraint_flag: %i", intra_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "one_picture_only_constraint_flag: %i",
one_picture_only_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "lower_bit_rate_constraint_flag: %i",
lower_bit_rate_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_14bit_constraint_flag: %i", max_14bit_constraint_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_33bits: %" PRIu64 "", reserved_zero_33bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_34bits: %" PRIu64 "", reserved_zero_34bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_7bits: %" PRIu32 "", reserved_zero_7bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_35bits: %" PRIu64 "", reserved_zero_35bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_43bits: %" PRIu64 "", reserved_zero_43bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "inbld_flag: %i", inbld_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_bit: %i", reserved_zero_bit);
}
void H265ProfileTierLevelParser::ProfileTierLevelState::fdump(
FILE* outfp, int indent_level) const {
fprintf(outfp, "profile_tier_level {");
indent_level = indent_level_incr(indent_level);
if (profilePresentFlag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "general {");
indent_level = indent_level_incr(indent_level);
general->fdump(outfp, indent_level);
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "general_level_idc: %i", general_level_idc);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sub_layer_profile_present_flag {");
for (const uint32_t& v : sub_layer_profile_present_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sub_layer_level_present_flag {");
for (const uint32_t& v : sub_layer_level_present_flag) {
fprintf(outfp, " %i ", v);
}
fprintf(outfp, " }");
if (maxNumSubLayersMinus1 > 0) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "reserved_zero_2bits {");
for (const uint32_t& v : reserved_zero_2bits) {
fprintf(outfp, " %i ", v);
}
fprintf(outfp, " }");
}
if (maxNumSubLayersMinus1 > 0) {
for (auto& v : sub_layer) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sub_layer {");
indent_level = indent_level_incr(indent_level);
v->fdump(outfp, indent_level);
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sub_layer_level_idc {");
for (const uint32_t& v : sub_layer_level_idc) {
fprintf(outfp, " %i ", v);
}
fprintf(outfp, " }");
}
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
6.解析出SPS参数集:
根据Rec. ITU-T H.265文档中的7.3.2.2中的VPS参数集:
SPS参数总集:



根据上参数总集表,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_sps_parser.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdio.h>
#include <cmath>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
#include "../h265include/h265nal/h265_profile_tier_level_parser.h"
#include "../h265include/h265nal/h265_scaling_list_data_parser.h"
#include "../h265include/h265nal/h265_vui_parameters_parser.h"
namespace {
typedef std::shared_ptr<h265nal::H265SpsParser::SpsState> SharedPtrSps;
} // namespace
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse SPS state from the supplied buffer.
std::shared_ptr<H265SpsParser::SpsState> H265SpsParser::ParseSps(
const uint8_t* data, size_t length) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseSps(&bit_buffer);
}
std::shared_ptr<H265SpsParser::SpsState> H265SpsParser::ParseSps(
rtc::BitBuffer* bit_buffer) noexcept {
uint32_t bits_tmp;
uint32_t golomb_tmp;
// H265 SPS Nal Unit (seq_parameter_set_rbsp()) parser.
// Section 7.3.2.2 ("Sequence parameter set data syntax") of the H.265
// standard for a complete description.
auto sps = std::make_shared<SpsState>();
// sps_video_parameter_set_id u(4)
if (!bit_buffer->ReadBits(&(sps->sps_video_parameter_set_id), 4)) {
return nullptr;
}
// sps_max_sub_layers_minus1 u(3)
if (!bit_buffer->ReadBits(&(sps->sps_max_sub_layers_minus1), 3)) {
return nullptr;
}
// sps_temporal_id_nesting_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_temporal_id_nesting_flag), 1)) {
return nullptr;
}
// profile_tier_level(1, sps_max_sub_layers_minus1)
sps->profile_tier_level = H265ProfileTierLevelParser::ParseProfileTierLevel(
bit_buffer, true, sps->sps_max_sub_layers_minus1);
if (sps->profile_tier_level == nullptr) {
return nullptr;
}
// sps_seq_parameter_set_id ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->sps_seq_parameter_set_id))) {
return nullptr;
}
// chroma_format_idc ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->chroma_format_idc))) {
return nullptr;
}
if (sps->chroma_format_idc == 3) {
// separate_colour_plane_flag u(1)
if (!bit_buffer->ReadBits(&(sps->separate_colour_plane_flag), 1)) {
return nullptr;
}
}
// pic_width_in_luma_samples ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->pic_width_in_luma_samples))) {
return nullptr;
}
// Rec. ITU-T H.265 v5 (02/2018) Page 78
// "pic_width_in_luma_samples shall not be equal to 0 and shall be an
// integer multiple of MinCbSizeY."
uint32_t MinCbSizeY = sps->getMinCbSizeY();
if ((sps->pic_width_in_luma_samples == 0) ||
((MinCbSizeY * (sps->pic_width_in_luma_samples / MinCbSizeY)) !=
sps->pic_width_in_luma_samples)) {
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: invalid sps->pic_width_in_luma_samples: %i\n",
sps->pic_width_in_luma_samples);
#endif // FPRINT_ERRORS
return nullptr;
}
// pic_height_in_luma_samples ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->pic_height_in_luma_samples))) {
return nullptr;
}
// Rec. ITU-T H.265 v5 (02/2018) Page 78
// "pic_height_in_luma_samples shall not be equal to 0 and shall be an
// integer multiple of MinCbSizeY."
if ((sps->pic_height_in_luma_samples == 0) ||
((MinCbSizeY * (sps->pic_height_in_luma_samples / MinCbSizeY)) !=
sps->pic_height_in_luma_samples)) {
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: invalid sps->pic_height_in_luma_samples: %i\n",
sps->pic_height_in_luma_samples);
#endif // FPRINT_ERRORS
return nullptr;
}
// conformance_window_flag u(1)
if (!bit_buffer->ReadBits(&(sps->conformance_window_flag), 1)) {
return nullptr;
}
if (sps->conformance_window_flag) {
// conf_win_left_offset ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->conf_win_left_offset))) {
return nullptr;
}
// conf_win_right_offset ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->conf_win_right_offset))) {
return nullptr;
}
// conf_win_top_offset ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->conf_win_top_offset))) {
return nullptr;
}
// conf_win_bottom_offset ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->conf_win_bottom_offset))) {
return nullptr;
}
}
// bit_depth_luma_minus8 ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->bit_depth_luma_minus8))) {
return nullptr;
}
// bit_depth_chroma_minus8 ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(sps->bit_depth_chroma_minus8))) {
return nullptr;
}
// log2_max_pic_order_cnt_lsb_minus4 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_max_pic_order_cnt_lsb_minus4))) {
return nullptr;
}
// sps_sub_layer_ordering_info_present_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_sub_layer_ordering_info_present_flag),
1)) {
return nullptr;
}
for (uint32_t i = (sps->sps_sub_layer_ordering_info_present_flag
? 0
: sps->sps_max_sub_layers_minus1);
i <= sps->sps_max_sub_layers_minus1; i++) {
// sps_max_dec_pic_buffering_minus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
// Section 7.4.3.2.1
// "The value of sps_max_dec_pic_buffering_minus1[i] shall be in the
// range of 0 to MaxDpbSize - 1, inclusive"
if (golomb_tmp >= h265limits::HEVC_MAX_DPB_SIZE) {
return nullptr;
}
sps->sps_max_dec_pic_buffering_minus1.push_back(golomb_tmp);
// sps_max_num_reorder_pics[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
sps->sps_max_num_reorder_pics.push_back(golomb_tmp);
// sps_max_latency_increase_plus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
sps->sps_max_latency_increase_plus1.push_back(golomb_tmp);
}
// log2_min_luma_coding_block_size_minus3 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_min_luma_coding_block_size_minus3))) {
return nullptr;
}
if (sps->log2_min_luma_coding_block_size_minus3 > 3) {
return nullptr;
}
// log2_diff_max_min_luma_coding_block_size ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_diff_max_min_luma_coding_block_size))) {
return nullptr;
}
if (sps->log2_diff_max_min_luma_coding_block_size > 3) {
return nullptr;
}
// log2_min_luma_transform_block_size_minus2 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_min_luma_transform_block_size_minus2))) {
return nullptr;
}
// log2_diff_max_min_luma_transform_block_size ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_diff_max_min_luma_transform_block_size))) {
return nullptr;
}
// max_transform_hierarchy_depth_inter ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->max_transform_hierarchy_depth_inter))) {
return nullptr;
}
// max_transform_hierarchy_depth_intra ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->max_transform_hierarchy_depth_intra))) {
return nullptr;
}
// scaling_list_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->scaling_list_enabled_flag), 1)) {
return nullptr;
}
if (sps->scaling_list_enabled_flag) {
// sps_scaling_list_data_present_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_scaling_list_data_present_flag), 1)) {
return nullptr;
}
if (sps->sps_scaling_list_data_present_flag) {
// scaling_list_data()
sps->scaling_list_data =
H265ScalingListDataParser::ParseScalingListData(bit_buffer);
if (sps->scaling_list_data == nullptr) {
return nullptr;
}
}
}
// amp_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->amp_enabled_flag), 1)) {
return nullptr;
}
// sample_adaptive_offset_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sample_adaptive_offset_enabled_flag), 1)) {
return nullptr;
}
// pcm_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->pcm_enabled_flag), 1)) {
return nullptr;
}
if (sps->pcm_enabled_flag) {
// pcm_sample_bit_depth_luma_minus1 u(4)
if (!bit_buffer->ReadBits(&(sps->pcm_sample_bit_depth_luma_minus1), 4)) {
return nullptr;
}
// pcm_sample_bit_depth_chroma_minus1 u(4)
if (!bit_buffer->ReadBits(&(sps->pcm_sample_bit_depth_chroma_minus1), 4)) {
return nullptr;
}
// log2_min_pcm_luma_coding_block_size_minus3 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_min_pcm_luma_coding_block_size_minus3))) {
return nullptr;
}
// log2_diff_max_min_pcm_luma_coding_block_size ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->log2_diff_max_min_pcm_luma_coding_block_size))) {
return nullptr;
}
// pcm_loop_filter_disabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->pcm_loop_filter_disabled_flag), 1)) {
return nullptr;
}
}
// num_short_term_ref_pic_sets
if (!bit_buffer->ReadExponentialGolomb(&(sps->num_short_term_ref_pic_sets))) {
return nullptr;
}
if (sps->num_short_term_ref_pic_sets >
h265limits::NUM_SHORT_TERM_REF_PIC_SETS_MAX) {
#ifdef FPRINT_ERRORS
fprintf(stderr,
"error: sps->num_short_term_ref_pic_sets == %" PRIu32
" > h265limits::NUM_SHORT_TERM_REF_PIC_SETS_MAX\n",
sps->num_short_term_ref_pic_sets);
#endif // FPRINT_ERRORS
return nullptr;
}
for (uint32_t i = 0; i < sps->num_short_term_ref_pic_sets; i++) {
uint32_t max_num_pics = 0;
if (!sps->getMaxNumPics(&max_num_pics)) {
return nullptr;
}
// st_ref_pic_set(i)
auto st_ref_pic_set_item = H265StRefPicSetParser::ParseStRefPicSet(
bit_buffer, i, sps->num_short_term_ref_pic_sets, &(sps->st_ref_pic_set),
max_num_pics);
if (st_ref_pic_set_item == nullptr) {
// not enough bits for the st_ref_pic_set
return nullptr;
}
sps->st_ref_pic_set.push_back(std::move(st_ref_pic_set_item));
}
// long_term_ref_pics_present_flag u(1)
if (!bit_buffer->ReadBits(&(sps->long_term_ref_pics_present_flag), 1)) {
return nullptr;
}
if (sps->long_term_ref_pics_present_flag) {
// num_long_term_ref_pics_sps ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(sps->num_long_term_ref_pics_sps))) {
return nullptr;
}
for (uint32_t i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
// lt_ref_pic_poc_lsb_sps[i] u(v) log2_max_pic_order_cnt_lsb_minus4 + 4
if (!bit_buffer->ReadBits(&bits_tmp,
sps->log2_max_pic_order_cnt_lsb_minus4 + 4)) {
return nullptr;
}
sps->lt_ref_pic_poc_lsb_sps.push_back(bits_tmp);
// used_by_curr_pic_lt_sps_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
sps->used_by_curr_pic_lt_sps_flag.push_back(bits_tmp);
}
}
// sps_temporal_mvp_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_temporal_mvp_enabled_flag), 1)) {
return nullptr;
}
// strong_intra_smoothing_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(sps->strong_intra_smoothing_enabled_flag), 1)) {
return nullptr;
}
// vui_parameters_present_flag u(1)
if (!bit_buffer->ReadBits(&(sps->vui_parameters_present_flag), 1)) {
return nullptr;
}
if (sps->vui_parameters_present_flag) {
// vui_parameters()
sps->vui_parameters = H265VuiParametersParser::ParseVuiParameters(
bit_buffer, sps->sps_max_sub_layers_minus1);
if (sps->vui_parameters == nullptr) {
return nullptr;
}
}
// sps_extension_present_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_extension_present_flag), 1)) {
return nullptr;
}
if (sps->sps_extension_present_flag) {
// sps_range_extension_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_range_extension_flag), 1)) {
return nullptr;
}
// sps_multilayer_extension_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_multilayer_extension_flag), 1)) {
return nullptr;
}
// sps_3d_extension_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_3d_extension_flag), 1)) {
return nullptr;
}
// sps_scc_extension_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_scc_extension_flag), 1)) {
return nullptr;
}
// sps_extension_4bits u(4)
if (!bit_buffer->ReadBits(&(sps->sps_extension_4bits), 4)) {
return nullptr;
}
}
if (sps->sps_range_extension_flag) {
// sps_range_extension()
sps->sps_range_extension =
H265SpsRangeExtensionParser::ParseSpsRangeExtension(bit_buffer);
if (sps->sps_range_extension == nullptr) {
return nullptr;
}
}
if (sps->sps_multilayer_extension_flag) {
// sps_multilayer_extension() // specified in Annex F
sps->sps_multilayer_extension =
H265SpsMultilayerExtensionParser::ParseSpsMultilayerExtension(
bit_buffer);
if (sps->sps_multilayer_extension == nullptr) {
return nullptr;
}
}
if (sps->sps_3d_extension_flag) {
// sps_3d_extension() // specified in Annex I
sps->sps_3d_extension =
H265Sps3dExtensionParser::ParseSps3dExtension(bit_buffer);
if (sps->sps_3d_extension == nullptr) {
return nullptr;
}
}
if (sps->sps_scc_extension_flag) {
// sps_scc_extension()
sps->sps_scc_extension = H265SpsSccExtensionParser::ParseSpsSccExtension(
bit_buffer, sps->chroma_format_idc, sps->bit_depth_luma_minus8,
sps->bit_depth_chroma_minus8);
if (sps->sps_scc_extension == nullptr) {
return nullptr;
}
}
if (sps->sps_extension_4bits) {
while (more_rbsp_data(bit_buffer)) {
// sps_extension_data_flag u(1)
if (!bit_buffer->ReadBits(&(sps->sps_extension_data_flag), 1)) {
return nullptr;
}
}
}
rbsp_trailing_bits(bit_buffer);
return sps;
}
#ifdef FDUMP_DEFINE
void H265SpsParser::SpsState::fdump(FILE* outfp, int indent_level) const {
fprintf(outfp, "sps {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_video_parameter_set_id: %i", sps_video_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_max_sub_layers_minus1: %i", sps_max_sub_layers_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_temporal_id_nesting_flag: %i",
sps_temporal_id_nesting_flag);
fdump_indent_level(outfp, indent_level);
profile_tier_level->fdump(outfp, indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_seq_parameter_set_id: %i", sps_seq_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "chroma_format_idc: %i", chroma_format_idc);
if (chroma_format_idc == 3) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "separate_colour_plane_flag: %i",
separate_colour_plane_flag);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pic_width_in_luma_samples: %i", pic_width_in_luma_samples);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pic_height_in_luma_samples: %i", pic_height_in_luma_samples);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "conformance_window_flag: %i", conformance_window_flag);
if (conformance_window_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "conf_win_left_offset: %i", conf_win_left_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "conf_win_right_offset: %i", conf_win_right_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "conf_win_top_offset: %i", conf_win_top_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "conf_win_bottom_offset: %i", conf_win_bottom_offset);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "bit_depth_luma_minus8: %i", bit_depth_luma_minus8);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "bit_depth_chroma_minus8: %i", bit_depth_chroma_minus8);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_max_pic_order_cnt_lsb_minus4: %i",
log2_max_pic_order_cnt_lsb_minus4);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_sub_layer_ordering_info_present_flag: %i",
sps_sub_layer_ordering_info_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_max_dec_pic_buffering_minus1 {");
for (const uint32_t& v : sps_max_dec_pic_buffering_minus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_max_num_reorder_pics {");
for (const uint32_t& v : sps_max_num_reorder_pics) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_max_latency_increase_plus1 {");
for (const uint32_t& v : sps_max_latency_increase_plus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_min_luma_coding_block_size_minus3: %i",
log2_min_luma_coding_block_size_minus3);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_diff_max_min_luma_coding_block_size: %i",
log2_diff_max_min_luma_coding_block_size);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_min_luma_transform_block_size_minus2: %i",
log2_min_luma_transform_block_size_minus2);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_diff_max_min_luma_transform_block_size: %i",
log2_diff_max_min_luma_transform_block_size);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_transform_hierarchy_depth_inter: %i",
max_transform_hierarchy_depth_inter);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "max_transform_hierarchy_depth_intra: %i",
max_transform_hierarchy_depth_intra);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "scaling_list_enabled_flag: %i", scaling_list_enabled_flag);
if (scaling_list_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_scaling_list_data_present_flag: %i",
sps_scaling_list_data_present_flag);
if (sps_scaling_list_data_present_flag) {
fdump_indent_level(outfp, indent_level);
scaling_list_data->fdump(outfp, indent_level);
}
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "amp_enabled_flag: %i", amp_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sample_adaptive_offset_enabled_flag: %i",
sample_adaptive_offset_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pcm_enabled_flag: %i", pcm_enabled_flag);
if (pcm_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pcm_sample_bit_depth_luma_minus1: %i",
pcm_sample_bit_depth_luma_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pcm_sample_bit_depth_chroma_minus1: %i",
pcm_sample_bit_depth_chroma_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_min_pcm_luma_coding_block_size_minus3: %i",
log2_min_pcm_luma_coding_block_size_minus3);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_diff_max_min_pcm_luma_coding_block_size: %i",
log2_diff_max_min_pcm_luma_coding_block_size);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pcm_loop_filter_disabled_flag: %i",
pcm_loop_filter_disabled_flag);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_short_term_ref_pic_sets: %i",
num_short_term_ref_pic_sets);
for (uint32_t i = 0; i < num_short_term_ref_pic_sets; i++) {
fdump_indent_level(outfp, indent_level);
st_ref_pic_set[i]->fdump(outfp, indent_level);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "long_term_ref_pics_present_flag: %i",
long_term_ref_pics_present_flag);
if (long_term_ref_pics_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_long_term_ref_pics_sps: %i",
num_long_term_ref_pics_sps);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "lt_ref_pic_poc_lsb_sps {");
for (const uint32_t& v : lt_ref_pic_poc_lsb_sps) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "used_by_curr_pic_lt_sps_flag {");
for (const uint32_t& v : used_by_curr_pic_lt_sps_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_temporal_mvp_enabled_flag: %i",
sps_temporal_mvp_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "strong_intra_smoothing_enabled_flag: %i",
strong_intra_smoothing_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "vui_parameters_present_flag: %i",
vui_parameters_present_flag);
if (vui_parameters_present_flag) {
fdump_indent_level(outfp, indent_level);
vui_parameters->fdump(outfp, indent_level);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_extension_present_flag: %i", sps_extension_present_flag);
if (sps_extension_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_range_extension_flag: %i", sps_range_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_multilayer_extension_flag: %i",
sps_multilayer_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_3d_extension_flag: %i", sps_3d_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_scc_extension_flag: %i", sps_scc_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sps_extension_4bits: %i", sps_extension_4bits);
}
if (sps_range_extension_flag) {
// sps_range_extension()
fdump_indent_level(outfp, indent_level);
sps_range_extension->fdump(outfp, indent_level);
}
if (sps_multilayer_extension_flag) {
// sps_multilayer_extension() // specified in Annex F
fdump_indent_level(outfp, indent_level);
sps_multilayer_extension->fdump(outfp, indent_level);
}
if (sps_3d_extension_flag) {
// sps_3d_extension() // specified in Annex I
fdump_indent_level(outfp, indent_level);
sps_3d_extension->fdump(outfp, indent_level);
}
if (sps_scc_extension_flag) {
// sps_scc_extension()
fdump_indent_level(outfp, indent_level);
sps_scc_extension->fdump(outfp, indent_level);
}
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
bool H265SpsParser::SpsState::getMaxNumPics(uint32_t* max_num_pics) noexcept {
if (sps_max_sub_layers_minus1 >= sps_max_dec_pic_buffering_minus1.size()) {
// Section 7.4.8: "num_negative_pics specifies the number of entries
// in the stRpsIdx-th candidate short-term RPS that have picture order
// count values less than the picture order count value of the current
// picture. When nuh_layer_id of the current picture is equal to 0, the
// value of num_negative_pics shall be in the range of 0 to
// sps_max_dec_pic_buffering_minus1[sps_max_sub_layers_minus1],
// inclusive."""
return false;
}
*max_num_pics = sps_max_dec_pic_buffering_minus1[sps_max_sub_layers_minus1];
return true;
}
uint32_t H265SpsParser::SpsState::getMinCbLog2SizeY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-10)
return log2_min_luma_coding_block_size_minus3 + 3;
}
uint32_t H265SpsParser::SpsState::getCtbLog2SizeY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-11)
return getMinCbLog2SizeY() + log2_diff_max_min_luma_coding_block_size;
}
uint32_t H265SpsParser::SpsState::getMinCbSizeY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-12)
return 1 << getMinCbLog2SizeY();
}
uint32_t H265SpsParser::SpsState::getCtbSizeY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-13)
return 1 << getCtbLog2SizeY();
}
uint32_t H265SpsParser::SpsState::getPicWidthInMinCbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-14)
return pic_width_in_luma_samples / getMinCbSizeY();
}
uint32_t H265SpsParser::SpsState::getPicWidthInCtbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 79, Equation (7-15)
return static_cast<uint32_t>(
std::ceil(1.0 * pic_width_in_luma_samples / getCtbSizeY()));
}
uint32_t H265SpsParser::SpsState::getPicHeightInMinCbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-16)
return pic_height_in_luma_samples / getMinCbSizeY();
}
uint32_t H265SpsParser::SpsState::getPicHeightInCtbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-17)
return static_cast<uint32_t>(
std::ceil(1.0 * pic_height_in_luma_samples / getCtbSizeY()));
}
uint32_t H265SpsParser::SpsState::getPicSizeInMinCbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-18)
return getPicWidthInMinCbsY() * getPicHeightInMinCbsY();
}
uint32_t H265SpsParser::SpsState::getPicSizeInCtbsY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-19)
return getPicWidthInCtbsY() * getPicHeightInCtbsY();
}
uint32_t H265SpsParser::SpsState::getPicSizeInSamplesY() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-20)
return pic_width_in_luma_samples * pic_height_in_luma_samples;
}
#if 0
uint32_t H265SpsParser::SpsState::getPicWidthInSamplesC() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-21)
return pic_width_in_luma_samples / getSubWidthC();
}
uint32_t H265SpsParser::SpsState::getPicHeightInSamplesC() noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 80, Equation (7-22)
return pic_height_in_luma_samples / getSubHeightC();
}
#endif
} // namespace h265nal
7.解析出PPS参数集:
根据Rec. ITU-T H.265文档中的7.3.2.3中的PPS参数集:
PPS参数总集:


根据上参数总集表,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_pps_parser.h"
#include <stdio.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
#include "../h265include/h265nal/h265_pps_scc_extension_parser.h"
#include "../h265include/h265nal/h265_profile_tier_level_parser.h"
#include "../h265include/h265nal/h265_scaling_list_data_parser.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse PPS state from the supplied buffer.
std::shared_ptr<H265PpsParser::PpsState> H265PpsParser::ParsePps(
const uint8_t* data, size_t length) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParsePps(&bit_buffer);
}
std::shared_ptr<H265PpsParser::PpsState> H265PpsParser::ParsePps(
rtc::BitBuffer* bit_buffer) noexcept {
uint32_t golomb_tmp;
// H265 PPS NAL Unit (pic_parameter_set_rbsp()) parser.
// Section 7.3.2.3 ("Picture parameter set data syntax") of the H.265
// standard for a complete description.
auto pps = std::make_shared<PpsState>();
// pps_pic_parameter_set_id ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(pps->pps_pic_parameter_set_id))) {
return nullptr;
}
// pps_seq_parameter_set_id ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(pps->pps_seq_parameter_set_id))) {
return nullptr;
}
// dependent_slice_segments_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->dependent_slice_segments_enabled_flag), 1)) {
return nullptr;
}
// output_flag_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->output_flag_present_flag), 1)) {
return nullptr;
}
// num_extra_slice_header_bits u(3)
if (!bit_buffer->ReadBits(&(pps->num_extra_slice_header_bits), 3)) {
return nullptr;
}
// sign_data_hiding_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->sign_data_hiding_enabled_flag), 1)) {
return nullptr;
}
// cabac_init_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->cabac_init_present_flag), 1)) {
return nullptr;
}
// num_ref_idx_l0_default_active_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(pps->num_ref_idx_l0_default_active_minus1))) {
return nullptr;
}
// num_ref_idx_l1_default_active_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(pps->num_ref_idx_l1_default_active_minus1))) {
return nullptr;
}
// init_qp_minus26 se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(&(pps->init_qp_minus26))) {
return nullptr;
}
// constrained_intra_pred_flag u(1)
if (!bit_buffer->ReadBits(&(pps->constrained_intra_pred_flag), 1)) {
return nullptr;
}
// transform_skip_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->transform_skip_enabled_flag), 1)) {
return nullptr;
}
// cu_qp_delta_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->cu_qp_delta_enabled_flag), 1)) {
return nullptr;
}
if (pps->cu_qp_delta_enabled_flag) {
// diff_cu_qp_delta_depth ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(pps->diff_cu_qp_delta_depth))) {
return nullptr;
}
}
// pps_cb_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(&(pps->pps_cb_qp_offset))) {
return nullptr;
}
// pps_cr_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(&(pps->pps_cr_qp_offset))) {
return nullptr;
}
// pps_slice_chroma_qp_offsets_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_slice_chroma_qp_offsets_present_flag),
1)) {
return nullptr;
}
// weighted_pred_flag u(1)
if (!bit_buffer->ReadBits(&(pps->weighted_pred_flag), 1)) {
return nullptr;
}
// weighted_bipred_flag u(1)
if (!bit_buffer->ReadBits(&(pps->weighted_bipred_flag), 1)) {
return nullptr;
}
// transquant_bypass_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->transquant_bypass_enabled_flag), 1)) {
return nullptr;
}
// tiles_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->tiles_enabled_flag), 1)) {
return nullptr;
}
// entropy_coding_sync_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->entropy_coding_sync_enabled_flag), 1)) {
return nullptr;
}
if (pps->tiles_enabled_flag) {
// num_tile_columns_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(pps->num_tile_columns_minus1))) {
return nullptr;
}
// num_tile_rows_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(&(pps->num_tile_rows_minus1))) {
return nullptr;
}
// uniform_spacing_flag u(1)
if (!bit_buffer->ReadBits(&(pps->uniform_spacing_flag), 1)) {
return nullptr;
}
if (!pps->uniform_spacing_flag) {
for (uint32_t i = 0; i < pps->num_tile_columns_minus1; i++) {
// column_width_minus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
pps->column_width_minus1.push_back(golomb_tmp);
}
for (uint32_t i = 0; i < pps->num_tile_rows_minus1; i++) {
// row_height_minus1[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
pps->row_height_minus1.push_back(golomb_tmp);
}
}
// loop_filter_across_tiles_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->loop_filter_across_tiles_enabled_flag),
1)) {
return nullptr;
}
}
// pps_loop_filter_across_slices_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_loop_filter_across_slices_enabled_flag),
1)) {
return nullptr;
}
// deblocking_filter_control_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->deblocking_filter_control_present_flag),
1)) {
return nullptr;
}
if (pps->deblocking_filter_control_present_flag) {
// deblocking_filter_override_enabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->deblocking_filter_override_enabled_flag),
1)) {
return nullptr;
}
// pps_deblocking_filter_disabled_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_deblocking_filter_disabled_flag), 1)) {
return nullptr;
}
if (!pps->pps_deblocking_filter_disabled_flag) {
// pps_beta_offset_div2 se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(pps->pps_beta_offset_div2))) {
return nullptr;
}
// pps_tc_offset_div2 se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(pps->pps_tc_offset_div2))) {
return nullptr;
}
}
}
// pps_scaling_list_data_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_scaling_list_data_present_flag), 1)) {
return nullptr;
}
if (pps->pps_scaling_list_data_present_flag) {
// scaling_list_data()
pps->scaling_list_data =
H265ScalingListDataParser::ParseScalingListData(bit_buffer);
if (pps->scaling_list_data == nullptr) {
return nullptr;
}
}
// lists_modification_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->lists_modification_present_flag), 1)) {
return nullptr;
}
// log2_parallel_merge_level_minus2 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(pps->log2_parallel_merge_level_minus2))) {
return nullptr;
}
// slice_segment_header_extension_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->slice_segment_header_extension_present_flag),
1)) {
return nullptr;
}
// pps_extension_present_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_extension_present_flag), 1)) {
return nullptr;
}
if (pps->pps_extension_present_flag) {
// pps_range_extension_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_range_extension_flag), 1)) {
return nullptr;
}
// pps_multilayer_extension_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_multilayer_extension_flag), 1)) {
return nullptr;
}
// pps_3d_extension_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_3d_extension_flag), 1)) {
return nullptr;
}
// pps_scc_extension_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_scc_extension_flag), 1)) {
return nullptr;
}
// pps_extension_4bits u(4)
if (!bit_buffer->ReadBits(&(pps->pps_extension_4bits), 4)) {
return nullptr;
}
}
if (pps->pps_range_extension_flag) {
// pps_range_extension()
// TODO(chemag): add support for pps_range_extension()
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: unimplemented pps_range_extension() in pps\n");
#endif // FPRINT_ERRORS
return nullptr;
}
if (pps->pps_multilayer_extension_flag) {
// pps_multilayer_extension() // specified in Annex F
// TODO(chemag): add support for pps_multilayer_extension()
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: unimplemented pps_multilayer_extension() in pps\n");
#endif // FPRINT_ERRORS
return nullptr;
}
if (pps->pps_3d_extension_flag) {
// pps_3d_extension() // specified in Annex I
// TODO(chemag): add support for pps_3d_extension()
#ifdef FPRINT_ERRORS
fprintf(stderr, "error: unimplemented pps_3d_extension() in pps\n");
#endif // FPRINT_ERRORS
return nullptr;
}
if (pps->pps_scc_extension_flag) {
// pps_range_extension()
pps->pps_scc_extension =
H265PpsSccExtensionParser::ParsePpsSccExtension(bit_buffer);
if (pps->pps_scc_extension == nullptr) {
return nullptr;
}
}
if (pps->pps_extension_4bits) {
while (more_rbsp_data(bit_buffer)) {
// pps_extension_data_flag u(1)
if (!bit_buffer->ReadBits(&(pps->pps_extension_data_flag), 1)) {
return nullptr;
}
}
}
rbsp_trailing_bits(bit_buffer);
return pps;
}
#ifdef FDUMP_DEFINE
void H265PpsParser::PpsState::fdump(FILE* outfp, int indent_level) const {
fprintf(outfp, "pps {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_pic_parameter_set_id: %i", pps_pic_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_seq_parameter_set_id: %i", pps_seq_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "dependent_slice_segments_enabled_flag: %i",
dependent_slice_segments_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "output_flag_present_flag: %i", output_flag_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_extra_slice_header_bits: %i",
num_extra_slice_header_bits);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "sign_data_hiding_enabled_flag: %i",
sign_data_hiding_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cabac_init_present_flag: %i", cabac_init_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_ref_idx_l0_default_active_minus1: %i",
num_ref_idx_l0_default_active_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_ref_idx_l1_default_active_minus1: %i",
num_ref_idx_l1_default_active_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "init_qp_minus26: %i", init_qp_minus26);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "constrained_intra_pred_flag: %i",
constrained_intra_pred_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "transform_skip_enabled_flag: %i",
transform_skip_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cu_qp_delta_enabled_flag: %i", cu_qp_delta_enabled_flag);
if (cu_qp_delta_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cu_qp_delta_enabled_flag: %i", cu_qp_delta_enabled_flag);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_cb_qp_offset: %i", pps_cb_qp_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_cr_qp_offset: %i", pps_cr_qp_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_slice_chroma_qp_offsets_present_flag: %i",
pps_slice_chroma_qp_offsets_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "weighted_pred_flag: %i", weighted_pred_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "weighted_bipred_flag: %i", weighted_bipred_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "transquant_bypass_enabled_flag: %i",
transquant_bypass_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "tiles_enabled_flag: %i", tiles_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "entropy_coding_sync_enabled_flag: %i",
entropy_coding_sync_enabled_flag);
if (tiles_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_tile_columns_minus1: %i", num_tile_columns_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_tile_rows_minus1: %i", num_tile_rows_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "uniform_spacing_flag: %i", uniform_spacing_flag);
if (!uniform_spacing_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "column_width_minus1 {");
for (const uint32_t& v : column_width_minus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "row_height_minus1 {");
for (const uint32_t& v : row_height_minus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "loop_filter_across_tiles_enabled_flag: %i",
loop_filter_across_tiles_enabled_flag);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_loop_filter_across_slices_enabled_flag: %i",
pps_loop_filter_across_slices_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "deblocking_filter_control_present_flag: %i",
deblocking_filter_control_present_flag);
if (deblocking_filter_control_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "deblocking_filter_override_enabled_flag: %i",
deblocking_filter_override_enabled_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_deblocking_filter_disabled_flag: %i",
pps_deblocking_filter_disabled_flag);
if (!pps_deblocking_filter_disabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_beta_offset_div2: %i", pps_beta_offset_div2);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_tc_offset_div2: %i", pps_tc_offset_div2);
}
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_scaling_list_data_present_flag: %i",
pps_scaling_list_data_present_flag);
if (pps_scaling_list_data_present_flag && scaling_list_data) {
fdump_indent_level(outfp, indent_level);
scaling_list_data->fdump(outfp, indent_level);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "lists_modification_present_flag: %i",
lists_modification_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "log2_parallel_merge_level_minus2: %i",
log2_parallel_merge_level_minus2);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_segment_header_extension_present_flag: %i",
slice_segment_header_extension_present_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_extension_present_flag: %i", pps_extension_present_flag);
if (pps_extension_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_range_extension_flag: %i", pps_range_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_multilayer_extension_flag: %i",
pps_multilayer_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_3d_extension_flag: %i", pps_3d_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_scc_extension_flag: %i", pps_scc_extension_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pps_extension_4bits: %i", pps_extension_4bits);
}
if (pps_range_extension_flag) {
// pps_range_extension()
// TODO(chemag): add support for pps_range_extension()
fprintf(stderr, "error: unimplemented pps_range_extension() in pps\n");
}
if (pps_multilayer_extension_flag) {
// pps_multilayer_extension() // specified in Annex F
// TODO(chemag): add support for pps_multilayer_extension()
fprintf(stderr,
"error: unimplemented pps_multilayer_extension_flag() in pps\n");
}
if (pps_3d_extension_flag) {
// pps_3d_extension() // specified in Annex I
// TODO(chemag): add support for pps_3d_extension()
fprintf(stderr, "error: unimplemented pps_3d_extension_flag() in pps\n");
}
if (pps_scc_extension_flag) {
// pps_scc_extension()
fdump_indent_level(outfp, indent_level);
pps_scc_extension->fdump(outfp, indent_level);
}
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
8、其他,例如Slice:
Slice总表:

根据上参数总集表,可实现如下代码:
cpp
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
#include "../h265include/h265nal/h265_slice_parser.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdio.h>
#include <cmath>
#include <cstdint>
#include <memory>
#include <vector>
#include "../h265include/h265nal/h265_common.h"
#include "../h265include/h265nal/h265_pred_weight_table_parser.h"
#include "../h265include/h265nal/h265_st_ref_pic_set_parser.h"
namespace h265nal {
// General note: this is based off the 2016/12 version of the H.265 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.265
// Unpack RBSP and parse slice segment state from the supplied buffer.
std::unique_ptr<H265SliceSegmentLayerParser::SliceSegmentLayerState>
H265SliceSegmentLayerParser::ParseSliceSegmentLayer(
const uint8_t* data, size_t length, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseSliceSegmentLayer(&bit_buffer, nal_unit_type,
bitstream_parser_state);
}
std::unique_ptr<H265SliceSegmentLayerParser::SliceSegmentLayerState>
H265SliceSegmentLayerParser::ParseSliceSegmentLayer(
rtc::BitBuffer* bit_buffer, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
// H265 slice segment layer (slice_segment_layer_rbsp()) NAL Unit.
// Section 7.3.2.9 ("Slice segment layer RBSP syntax") of the H.265
// standard for a complete description.
auto slice_segment_layer = std::make_unique<SliceSegmentLayerState>();
// input parameters
slice_segment_layer->nal_unit_type = nal_unit_type;
// slice_segment_header()
slice_segment_layer->slice_segment_header =
H265SliceSegmentHeaderParser::ParseSliceSegmentHeader(
bit_buffer, nal_unit_type, bitstream_parser_state);
if (slice_segment_layer->slice_segment_header == nullptr) {
return nullptr;
}
// slice_segment_data()
// rbsp_slice_segment_trailing_bits()
return slice_segment_layer;
}
#ifdef FDUMP_DEFINE
void H265SliceSegmentLayerParser::SliceSegmentLayerState::fdump(
FILE* outfp, int indent_level) const {
fprintf(outfp, "slice_segment_layer {");
indent_level = indent_level_incr(indent_level);
if (slice_segment_header != nullptr) {
fdump_indent_level(outfp, indent_level);
slice_segment_header->fdump(outfp, indent_level);
}
// slice_segment_data()
// rbsp_slice_segment_trailing_bits()
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
// Unpack RBSP and parse slice segment header state from the supplied buffer.
std::unique_ptr<H265SliceSegmentHeaderParser::SliceSegmentHeaderState>
H265SliceSegmentHeaderParser::ParseSliceSegmentHeader(
const uint8_t* data, size_t length, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
std::vector<uint8_t> unpacked_buffer = UnescapeRbsp(data, length);
rtc::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());
return ParseSliceSegmentHeader(&bit_buffer, nal_unit_type,
bitstream_parser_state);
}
std::unique_ptr<H265SliceSegmentHeaderParser::SliceSegmentHeaderState>
H265SliceSegmentHeaderParser::ParseSliceSegmentHeader(
rtc::BitBuffer* bit_buffer, uint32_t nal_unit_type,
struct H265BitstreamParserState* bitstream_parser_state) noexcept {
uint32_t bits_tmp;
uint32_t golomb_tmp;
// H265 slice segment header (slice_segment_layer_rbsp()) NAL Unit.
// Section 7.3.6.1 ("General slice segment header syntax") of the H.265
// standard for a complete description.
auto slice_segment_header = std::make_unique<SliceSegmentHeaderState>();
// input parameters
slice_segment_header->nal_unit_type = nal_unit_type;
// first_slice_segment_in_pic_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->first_slice_segment_in_pic_flag), 1)) {
return nullptr;
}
if (slice_segment_header->nal_unit_type >= BLA_W_LP &&
slice_segment_header->nal_unit_type <= RSV_IRAP_VCL23) {
// no_output_of_prior_pics_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->no_output_of_prior_pics_flag), 1)) {
return nullptr;
}
}
// slice_pic_parameter_set_id ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->slice_pic_parameter_set_id))) {
return nullptr;
}
uint32_t pps_id = slice_segment_header->slice_pic_parameter_set_id;
if (bitstream_parser_state->pps.find(pps_id) ==
bitstream_parser_state->pps.end()) {
// non-existent PPS id
return nullptr;
}
auto& pps = bitstream_parser_state->pps[pps_id];
uint32_t sps_id =
bitstream_parser_state->pps[pps_id]->pps_seq_parameter_set_id;
if (bitstream_parser_state->sps.find(sps_id) ==
bitstream_parser_state->sps.end()) {
// non-existent SPS id
return nullptr;
}
auto& sps = bitstream_parser_state->sps[sps_id];
if (!slice_segment_header->first_slice_segment_in_pic_flag) {
slice_segment_header->dependent_slice_segments_enabled_flag =
pps->dependent_slice_segments_enabled_flag;
if (slice_segment_header->dependent_slice_segments_enabled_flag) {
// dependent_slice_segment_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->dependent_slice_segment_flag), 1)) {
return nullptr;
}
}
size_t PicSizeInCtbsY = sps->getPicSizeInCtbsY();
size_t slice_segment_address_len = static_cast<size_t>(
std::ceil(std::log2(static_cast<float>(PicSizeInCtbsY))));
// range: 0 to PicSizeInCtbsY - 1
// slice_segment_address u(v)
if (!bit_buffer->ReadBits(&(slice_segment_header->slice_segment_address),
slice_segment_address_len)) {
return nullptr;
}
}
if (!slice_segment_header->dependent_slice_segment_flag) {
slice_segment_header->num_extra_slice_header_bits =
pps->num_extra_slice_header_bits;
for (uint32_t i = 0; i < slice_segment_header->num_extra_slice_header_bits;
i++) {
// slice_reserved_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
slice_segment_header->slice_reserved_flag.push_back(bits_tmp);
}
// slice_type ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->slice_type))) {
return nullptr;
}
slice_segment_header->output_flag_present_flag =
pps->output_flag_present_flag;
if (slice_segment_header->output_flag_present_flag) {
// pic_output_flag u(1)
if (!bit_buffer->ReadBits(&(slice_segment_header->pic_output_flag), 1)) {
return nullptr;
}
}
slice_segment_header->separate_colour_plane_flag =
sps->separate_colour_plane_flag;
if (slice_segment_header->separate_colour_plane_flag == 1) {
// colour_plane_id u(2)
if (!bit_buffer->ReadBits(&(slice_segment_header->colour_plane_id), 2)) {
return nullptr;
}
}
if (slice_segment_header->nal_unit_type != IDR_W_RADL &&
slice_segment_header->nal_unit_type != IDR_N_LP) {
// length of the slice_pic_order_cnt_lsb syntax element is
// log2_max_pic_order_cnt_lsb_minus4 + 4 bits. The value of the
// slice_pic_order_cnt_lsb shall be in the range of 0 to
// MaxPicOrderCntLsb - 1, inclusive.
slice_segment_header->log2_max_pic_order_cnt_lsb_minus4 =
sps->log2_max_pic_order_cnt_lsb_minus4;
size_t slice_pic_order_cnt_lsb_len =
slice_segment_header->log2_max_pic_order_cnt_lsb_minus4 + 4;
// slice_pic_order_cnt_lsb u(v)
if (!bit_buffer->ReadBits(
&(slice_segment_header->slice_pic_order_cnt_lsb),
slice_pic_order_cnt_lsb_len)) {
return nullptr;
}
// short_term_ref_pic_set_sps_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->short_term_ref_pic_set_sps_flag), 1)) {
return nullptr;
}
slice_segment_header->num_short_term_ref_pic_sets =
sps->num_short_term_ref_pic_sets;
if (slice_segment_header->num_short_term_ref_pic_sets >
h265limits::NUM_SHORT_TERM_REF_PIC_SETS_MAX) {
#ifdef FPRINT_ERRORS
fprintf(stderr,
"error: slice_segment_header->num_short_term_ref_pic_sets == "
"%" PRIu32 " > h265limits::NUM_SHORT_TERM_REF_PIC_SETS_MAX\n",
slice_segment_header->num_short_term_ref_pic_sets);
#endif // FPRINT_ERRORS
return nullptr;
}
if (!slice_segment_header->short_term_ref_pic_set_sps_flag) {
// st_ref_pic_set(num_short_term_ref_pic_sets)
const auto& st_ref_pic_set = sps->st_ref_pic_set;
uint32_t max_num_pics = 0;
if (!sps->getMaxNumPics(&max_num_pics)) {
return nullptr;
}
slice_segment_header->st_ref_pic_set =
H265StRefPicSetParser::ParseStRefPicSet(
bit_buffer, slice_segment_header->num_short_term_ref_pic_sets,
slice_segment_header->num_short_term_ref_pic_sets,
&st_ref_pic_set, max_num_pics);
if (slice_segment_header->st_ref_pic_set == nullptr) {
return nullptr;
}
} else if (slice_segment_header->num_short_term_ref_pic_sets > 1) {
// Ceil(Log2(num_short_term_ref_pic_sets));
size_t short_term_ref_pic_set_idx_len =
static_cast<size_t>(std::ceil(std::log2(static_cast<float>(
slice_segment_header->num_short_term_ref_pic_sets))));
// short_term_ref_pic_set_idx u(v)
if (!bit_buffer->ReadBits(
&(slice_segment_header->short_term_ref_pic_set_idx),
short_term_ref_pic_set_idx_len)) {
return nullptr;
}
}
slice_segment_header->long_term_ref_pics_present_flag =
sps->long_term_ref_pics_present_flag;
if (slice_segment_header->long_term_ref_pics_present_flag) {
slice_segment_header->num_long_term_ref_pics_sps =
sps->num_long_term_ref_pics_sps;
if (slice_segment_header->num_long_term_ref_pics_sps > 0) {
// num_long_term_sps ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->num_long_term_sps))) {
return nullptr;
}
}
// num_long_term_pics ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->num_long_term_pics))) {
return nullptr;
}
for (uint32_t i = 0; i < slice_segment_header->num_long_term_sps +
slice_segment_header->num_long_term_pics;
i++) {
if (i < slice_segment_header->num_long_term_sps) {
if (slice_segment_header->num_long_term_ref_pics_sps > 1) {
// lt_idx_sps[i] u(v)
// number of bits used to represent lt_idx_sps[i] is equal to
// Ceil(Log2(num_long_term_ref_pics_sps)).
size_t lt_idx_sps_len =
static_cast<size_t>(std::ceil(std::log2(static_cast<float>(
slice_segment_header->num_long_term_ref_pics_sps))));
if (!bit_buffer->ReadBits(&bits_tmp, lt_idx_sps_len)) {
return nullptr;
}
slice_segment_header->lt_idx_sps.push_back(bits_tmp);
}
} else {
// poc_lsb_lt[i] u(v)
// length of the poc_lsb_lt[i] syntax element is
// log2_max_pic_order_cnt_lsb_minus4 + 4 bits. [...]
// value of lt_idx_sps[i] shall be in the range of 0 to
// num_long_term_ref_pics_sps - 1, inclusive.
size_t poc_lsb_lt_len =
slice_segment_header->log2_max_pic_order_cnt_lsb_minus4 + 4;
// slice_pic_order_cnt_lsb u(v)
if (!bit_buffer->ReadBits(&bits_tmp, poc_lsb_lt_len)) {
return nullptr;
}
slice_segment_header->poc_lsb_lt.push_back(bits_tmp);
// used_by_curr_pic_lt_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
slice_segment_header->used_by_curr_pic_lt_flag.push_back(bits_tmp);
}
// delta_poc_msb_present_flag[i] u(1)
if (!bit_buffer->ReadBits(&bits_tmp, 1)) {
return nullptr;
}
slice_segment_header->delta_poc_msb_present_flag.push_back(bits_tmp);
if (slice_segment_header->delta_poc_msb_present_flag[i]) {
// delta_poc_msb_cycle_lt[i] ue(v)
if (!bit_buffer->ReadExponentialGolomb(&golomb_tmp)) {
return nullptr;
}
slice_segment_header->delta_poc_msb_cycle_lt.push_back(golomb_tmp);
}
}
}
slice_segment_header->sps_temporal_mvp_enabled_flag =
sps->sps_temporal_mvp_enabled_flag;
if (slice_segment_header->sps_temporal_mvp_enabled_flag) {
// slice_temporal_mvp_enabled_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->slice_temporal_mvp_enabled_flag), 1)) {
return nullptr;
}
}
}
slice_segment_header->sample_adaptive_offset_enabled_flag =
sps->sample_adaptive_offset_enabled_flag;
if (slice_segment_header->sample_adaptive_offset_enabled_flag) {
// slice_sao_luma_flag u(1)
if (!bit_buffer->ReadBits(&(slice_segment_header->slice_sao_luma_flag),
1)) {
return nullptr;
}
// Depending on the value of separate_colour_plane_flag, the value of
// the variable ChromaArrayType is assigned as follows:
// - If separate_colour_plane_flag is equal to 0, ChromaArrayType is
// set equal to chroma_format_idc.
// - Otherwise (separate_colour_plane_flag is equal to 1),
// ChromaArrayType is set equal to 0.
uint32_t chroma_format_idc = sps->chroma_format_idc;
if (slice_segment_header->separate_colour_plane_flag == 0) {
slice_segment_header->ChromaArrayType = chroma_format_idc;
} else {
slice_segment_header->ChromaArrayType = 0;
}
if (slice_segment_header->ChromaArrayType != 0) {
// slice_sao_chroma_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->slice_sao_chroma_flag), 1)) {
return nullptr;
}
}
}
if (slice_segment_header->slice_type == SliceType_P ||
slice_segment_header->slice_type == SliceType_B) {
// num_ref_idx_active_override_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->num_ref_idx_active_override_flag), 1)) {
return nullptr;
}
if (slice_segment_header->num_ref_idx_active_override_flag) {
// num_ref_idx_l0_active_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->num_ref_idx_l0_active_minus1))) {
return nullptr;
}
if (slice_segment_header->slice_type == SliceType_B) {
// num_ref_idx_l1_active_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->num_ref_idx_l1_active_minus1))) {
return nullptr;
}
}
}
slice_segment_header->lists_modification_present_flag =
pps->lists_modification_present_flag;
// TODO(chemag): calculate NumPicTotalCurr support (page 99)
slice_segment_header->NumPicTotalCurr = 0;
if (slice_segment_header->lists_modification_present_flag &&
slice_segment_header->NumPicTotalCurr > 1) {
// ref_pic_lists_modification()
// TODO(chemag): add support for ref_pic_lists_modification()
#ifdef FPRINT_ERRORS
fprintf(stderr,
"error: unimplemented ref_pic_lists_modification in "
"slice_header\n");
#endif // FPRINT_ERRORS
}
if (slice_segment_header->slice_type == SliceType_B) {
// mvd_l1_zero_flag u(1)
if (!bit_buffer->ReadBits(&(slice_segment_header->mvd_l1_zero_flag),
1)) {
return nullptr;
}
}
slice_segment_header->cabac_init_present_flag =
pps->cabac_init_present_flag;
if (slice_segment_header->cabac_init_present_flag) {
// cabac_init_flag u(1)
if (!bit_buffer->ReadBits(&(slice_segment_header->cabac_init_flag),
1)) {
return nullptr;
}
}
if (slice_segment_header->slice_temporal_mvp_enabled_flag) {
if (slice_segment_header->slice_type == SliceType_B) {
// collocated_from_l0_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->collocated_from_l0_flag), 1)) {
return nullptr;
}
}
if ((slice_segment_header->collocated_from_l0_flag &&
slice_segment_header->num_ref_idx_l0_active_minus1 > 0) ||
(!slice_segment_header->collocated_from_l0_flag &&
slice_segment_header->num_ref_idx_l1_active_minus1 > 0)) {
// collocated_ref_idx ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->collocated_ref_idx))) {
return nullptr;
}
}
}
slice_segment_header->weighted_pred_flag = pps->weighted_pred_flag;
slice_segment_header->weighted_bipred_flag = pps->weighted_bipred_flag;
if ((slice_segment_header->weighted_pred_flag &&
slice_segment_header->slice_type == SliceType_P) ||
(slice_segment_header->weighted_bipred_flag &&
slice_segment_header->slice_type == SliceType_B)) {
// pred_weight_table()
slice_segment_header->pred_weight_table =
H265PredWeightTableParser::ParsePredWeightTable(
bit_buffer, slice_segment_header->ChromaArrayType,
slice_segment_header->num_ref_idx_l0_active_minus1);
if (slice_segment_header->pred_weight_table == nullptr) {
return nullptr;
}
}
// five_minus_max_num_merge_cand ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->five_minus_max_num_merge_cand))) {
return nullptr;
}
slice_segment_header->motion_vector_resolution_control_idc = 0;
if (sps->sps_scc_extension_flag) {
if (sps->sps_scc_extension != nullptr) {
slice_segment_header->motion_vector_resolution_control_idc =
sps->sps_scc_extension->motion_vector_resolution_control_idc;
}
}
if (slice_segment_header->motion_vector_resolution_control_idc == 2) {
// use_integer_mv_flag u(1)
if (!bit_buffer->ReadBits(&(slice_segment_header->use_integer_mv_flag),
1)) {
return nullptr;
}
}
}
// slice_qp_delta se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_qp_delta))) {
return nullptr;
}
slice_segment_header->pps_slice_chroma_qp_offsets_present_flag =
pps->pps_slice_chroma_qp_offsets_present_flag;
if (slice_segment_header->pps_slice_chroma_qp_offsets_present_flag) {
// slice_cb_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_cb_qp_offset))) {
return nullptr;
}
// slice_cr_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_cr_qp_offset))) {
return nullptr;
}
}
slice_segment_header->pps_slice_act_qp_offsets_present_flag = 0;
if (pps->pps_scc_extension_flag) {
slice_segment_header->pps_slice_act_qp_offsets_present_flag =
pps->pps_scc_extension->pps_slice_act_qp_offsets_present_flag;
}
if (slice_segment_header->pps_slice_act_qp_offsets_present_flag) {
// slice_act_y_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_act_y_qp_offset))) {
return nullptr;
}
// slice_act_cb_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_act_cb_qp_offset))) {
return nullptr;
}
// slice_act_cr_qp_offset se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_act_cr_qp_offset))) {
return nullptr;
}
}
// TODO(chemag): add support for pps_range_extension()
// slice_segment_header->chroma_qp_offset_list_enabled_flag =
// pps->pps_range_extension->chroma_qp_offset_list_enabled_flag;
slice_segment_header->chroma_qp_offset_list_enabled_flag = 0;
if (slice_segment_header->chroma_qp_offset_list_enabled_flag) {
// cu_chroma_qp_offset_enabled_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->cu_chroma_qp_offset_enabled_flag), 1)) {
return nullptr;
}
}
slice_segment_header->deblocking_filter_override_enabled_flag =
pps->deblocking_filter_override_enabled_flag;
if (slice_segment_header->deblocking_filter_override_enabled_flag) {
// deblocking_filter_override_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->deblocking_filter_override_flag), 1)) {
return nullptr;
}
}
if (slice_segment_header->deblocking_filter_override_flag) {
// slice_deblocking_filter_disabled_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header->slice_deblocking_filter_disabled_flag),
1)) {
return nullptr;
}
if (!slice_segment_header->slice_deblocking_filter_disabled_flag) {
// slice_beta_offset_div2 se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_beta_offset_div2))) {
return nullptr;
}
// slice_tc_offset_div2 se(v)
if (!bit_buffer->ReadSignedExponentialGolomb(
&(slice_segment_header->slice_tc_offset_div2))) {
return nullptr;
}
}
}
slice_segment_header->pps_loop_filter_across_slices_enabled_flag =
pps->pps_loop_filter_across_slices_enabled_flag;
if (slice_segment_header->pps_loop_filter_across_slices_enabled_flag &&
(slice_segment_header->slice_sao_luma_flag ||
slice_segment_header->slice_sao_chroma_flag ||
!slice_segment_header->slice_deblocking_filter_disabled_flag)) {
// slice_loop_filter_across_slices_enabled_flag u(1)
if (!bit_buffer->ReadBits(
&(slice_segment_header
->slice_loop_filter_across_slices_enabled_flag),
1)) {
return nullptr;
}
}
}
slice_segment_header->tiles_enabled_flag = pps->tiles_enabled_flag;
slice_segment_header->entropy_coding_sync_enabled_flag =
pps->entropy_coding_sync_enabled_flag;
if (slice_segment_header->tiles_enabled_flag ||
slice_segment_header->entropy_coding_sync_enabled_flag) {
// num_entry_point_offsets ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->num_entry_point_offsets))) {
return nullptr;
}
if (!slice_segment_header->isValidNumEntryPointOffsets(
slice_segment_header->num_entry_point_offsets, sps, pps)) {
#ifdef FPRINT_ERRORS
fprintf(stderr,
"error: invalid slice_segment_header->num_entry_point_offsets: "
"%" PRIu32 "\n",
slice_segment_header->num_entry_point_offsets);
return nullptr;
#endif // FPRINT_ERRORS
}
if (slice_segment_header->num_entry_point_offsets > 0) {
// offset_len_minus1 ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->offset_len_minus1))) {
return nullptr;
}
for (uint32_t i = 0; i < slice_segment_header->num_entry_point_offsets;
i++) {
// entry_point_offset_minus1[i] u(v)
if (!bit_buffer->ReadBits(
&bits_tmp, slice_segment_header->offset_len_minus1 + 1)) {
return nullptr;
}
slice_segment_header->entry_point_offset_minus1.push_back(bits_tmp);
}
}
}
slice_segment_header->slice_segment_header_extension_present_flag =
pps->slice_segment_header_extension_present_flag;
if (slice_segment_header->slice_segment_header_extension_present_flag) {
// slice_segment_header_extension_length ue(v)
if (!bit_buffer->ReadExponentialGolomb(
&(slice_segment_header->slice_segment_header_extension_length))) {
return nullptr;
}
for (uint32_t i = 0;
i < slice_segment_header->slice_segment_header_extension_length; i++) {
// slice_segment_header_extension_data_byte[i] u(8)
if (!bit_buffer->ReadBits(&bits_tmp, 8)) {
return nullptr;
}
slice_segment_header->slice_segment_header_extension_data_byte.push_back(
bits_tmp);
}
}
// TODO(chemag): implement byte_alignment()
// byte_alignment()
return slice_segment_header;
}
bool H265SliceSegmentHeaderParser::SliceSegmentHeaderState::
isValidNumEntryPointOffsets(
uint32_t num_entry_point_offsets_value,
std::shared_ptr<struct H265SpsParser::SpsState> sps,
std::shared_ptr<struct H265PpsParser::PpsState> pps) noexcept {
// Rec. ITU-T H.265 v5 (02/2018) Page 100
// The value of num_entry_point_offsets is constrained as follows:
// - If tiles_enabled_flag is equal to 0 and entropy_coding_sync_enabled_flag
// is equal to 1, the value of num_entry_point_offsets shall be in the
// range of 0 to PicHeightInCtbsY - 1, inclusive.
if (tiles_enabled_flag == 0 && entropy_coding_sync_enabled_flag == 1) {
return (num_entry_point_offsets_value <= (sps->getPicHeightInCtbsY() - 1));
}
// - Otherwise, if tiles_enabled_flag is equal to 1 and
// entropy_coding_sync_enabled_flag is equal to 0, the value of
// num_entry_point_offsets shall be in the range of 0 to
// ( num_tile_columns_minus1 + 1 ) * ( num_tile_rows_minus1 + 1 ) - 1,
// inclusive.
if (tiles_enabled_flag == 1 && entropy_coding_sync_enabled_flag == 0) {
uint32_t max_num_entry_point_offsets =
((pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1) -
1);
return (num_entry_point_offsets_value <= max_num_entry_point_offsets);
}
// - Otherwise, when tiles_enabled_flag is equal to 1 and
// entropy_coding_sync_enabled_flag is equal to 1, the value of
// num_entry_point_offsets shall be in the range of 0 to
// ( num_tile_columns_minus1 + 1 ) * PicHeightInCtbsY - 1, inclusive.
if (tiles_enabled_flag == 1 && entropy_coding_sync_enabled_flag == 1) {
uint32_t max_num_entry_point_offsets =
((pps->num_tile_columns_minus1 + 1) * (sps->getPicHeightInCtbsY() - 1));
return (num_entry_point_offsets_value <= max_num_entry_point_offsets);
}
// TODO(chemag): not clear what to do in other cases. As the same paragraph
// contains "When not present, the value of num_entry_point_offsets is
// inferred to be equal to 0.", we will check for zero here.
return (num_entry_point_offsets_value == 0);
}
#ifdef FDUMP_DEFINE
void H265SliceSegmentHeaderParser::SliceSegmentHeaderState::fdump(
FILE* outfp, int indent_level) const {
fprintf(outfp, "slice_segment_header {");
indent_level = indent_level_incr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "first_slice_segment_in_pic_flag: %i",
first_slice_segment_in_pic_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "no_output_of_prior_pics_flag: %i",
no_output_of_prior_pics_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_pic_parameter_set_id: %i", slice_pic_parameter_set_id);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "dependent_slice_segment_flag: %i",
dependent_slice_segment_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_segment_address: %i", slice_segment_address);
if (!dependent_slice_segment_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_reserved_flag {");
for (const uint32_t& v : slice_reserved_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_type: %i", slice_type);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "pic_output_flag: %i", pic_output_flag);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "colour_plane_id: %i", colour_plane_id);
if (nal_unit_type != IDR_W_RADL && nal_unit_type != IDR_N_LP) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_pic_order_cnt_lsb: %i", slice_pic_order_cnt_lsb);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "short_term_ref_pic_set_sps_flag: %i",
short_term_ref_pic_set_sps_flag);
if (!short_term_ref_pic_set_sps_flag) {
fdump_indent_level(outfp, indent_level);
if (st_ref_pic_set) {
st_ref_pic_set->fdump(outfp, indent_level);
}
} else if (num_short_term_ref_pic_sets > 1) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "short_term_ref_pic_set_idx: %i",
short_term_ref_pic_set_idx);
}
if (long_term_ref_pics_present_flag) {
if (num_long_term_ref_pics_sps > 0) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_long_term_sps: %i", num_long_term_sps);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_long_term_pics: %i", num_long_term_pics);
for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) {
if (i < num_long_term_sps) {
if (num_long_term_ref_pics_sps > 1) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "lt_idx_sps {");
for (const uint32_t& v : lt_idx_sps) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
} else {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "poc_lsb_lt {");
for (const uint32_t& v : poc_lsb_lt) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "used_by_curr_pic_lt_flag {");
for (const uint32_t& v : used_by_curr_pic_lt_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "delta_poc_msb_present_flag {");
for (const uint32_t& v : delta_poc_msb_present_flag) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "delta_poc_msb_cycle_lt {");
for (const uint32_t& v : delta_poc_msb_cycle_lt) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
}
if (sps_temporal_mvp_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_temporal_mvp_enabled_flag: %i",
slice_temporal_mvp_enabled_flag);
}
}
if (sample_adaptive_offset_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_sao_luma_flag: %i", slice_sao_luma_flag);
if (ChromaArrayType != 0) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_sao_chroma_flag: %i", slice_sao_chroma_flag);
}
}
if (slice_type == SliceType_P || slice_type == SliceType_B) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_ref_idx_active_override_flag: %i",
num_ref_idx_active_override_flag);
if (num_ref_idx_active_override_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_ref_idx_l0_active_minus1: %i",
num_ref_idx_l0_active_minus1);
if (slice_type == SliceType_B) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_ref_idx_l1_active_minus1: %i",
num_ref_idx_l1_active_minus1);
}
}
if (lists_modification_present_flag && NumPicTotalCurr > 1) {
// TODO(chemag): add support for ref_pic_lists_modification()
}
if (slice_type == SliceType_B) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "mvd_l1_zero_flag: %i", mvd_l1_zero_flag);
}
if (cabac_init_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cabac_init_flag: %i", cabac_init_flag);
}
if (slice_temporal_mvp_enabled_flag) {
if (slice_type == SliceType_B) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "collocated_from_l0_flag: %i",
collocated_from_l0_flag);
}
if ((collocated_from_l0_flag && num_ref_idx_l0_active_minus1 > 0) ||
(!collocated_from_l0_flag && num_ref_idx_l1_active_minus1 > 0)) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "collocated_ref_idx: %i", collocated_ref_idx);
}
}
if ((weighted_pred_flag && slice_type == SliceType_P) ||
(weighted_bipred_flag && slice_type == SliceType_B)) {
fdump_indent_level(outfp, indent_level);
pred_weight_table->fdump(outfp, indent_level);
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "five_minus_max_num_merge_cand: %i",
five_minus_max_num_merge_cand);
if (motion_vector_resolution_control_idc == 2) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "use_integer_mv_flag: %i", use_integer_mv_flag);
}
}
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_qp_delta: %i", slice_qp_delta);
if (pps_slice_chroma_qp_offsets_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_cb_qp_offset: %i", slice_cb_qp_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_cr_qp_offset: %i", slice_cr_qp_offset);
}
if (pps_slice_act_qp_offsets_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_act_y_qp_offset: %i", slice_act_y_qp_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_act_cb_qp_offset: %i", slice_act_cb_qp_offset);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_act_cr_qp_offset: %i", slice_act_cr_qp_offset);
}
if (chroma_qp_offset_list_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "cu_chroma_qp_offset_enabled_flag: %i",
cu_chroma_qp_offset_enabled_flag);
}
if (deblocking_filter_override_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "deblocking_filter_override_flag: %i",
deblocking_filter_override_flag);
}
if (deblocking_filter_override_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_deblocking_filter_disabled_flag: %i",
slice_deblocking_filter_disabled_flag);
if (!slice_deblocking_filter_disabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_beta_offset_div2: %i", slice_beta_offset_div2);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_tc_offset_div2: %i", slice_tc_offset_div2);
}
}
if (pps_loop_filter_across_slices_enabled_flag &&
(slice_sao_luma_flag || slice_sao_chroma_flag ||
!slice_deblocking_filter_disabled_flag)) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_loop_filter_across_slices_enabled_flag: %i",
slice_loop_filter_across_slices_enabled_flag);
}
}
if (tiles_enabled_flag || entropy_coding_sync_enabled_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "num_entry_point_offsets: %i", num_entry_point_offsets);
if (num_entry_point_offsets > 0) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "offset_len_minus1: %i", offset_len_minus1);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "entry_point_offset_minus1 {");
for (const uint32_t& v : entry_point_offset_minus1) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
}
if (slice_segment_header_extension_present_flag) {
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_segment_header_extension_length: %i",
slice_segment_header_extension_length);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "slice_segment_header_extension_data_byte {");
for (const uint32_t& v : slice_segment_header_extension_data_byte) {
fprintf(outfp, " %i", v);
}
fprintf(outfp, " }");
}
// byte_alignment()
indent_level = indent_level_decr(indent_level);
fdump_indent_level(outfp, indent_level);
fprintf(outfp, "}");
}
#endif // FDUMP_DEFINE
} // namespace h265nal
9、代码实现:
以上所有代码均在本博主github项目:WyFFmpeg/h265/src/main at main · wangyongyao1989/WyFFmpeg · GitHub中,感兴趣的读者可以自行下载。
参考资料:
《新一代高效视频编码 H.265/HEVC 原理、标准与实现》------万帅 杨付正 编著
H.265 : High efficiency video coding
WyFFmpeg/h265/src/main at main · wangyongyao1989/WyFFmpeg · GitHub