00 00 00 01是Start code后面的ox67为 0110 0111
forbidden_zero_bit 是禁止位,应该是第一位即f(1)=0,1为语法有错误
nal_ref_idc是参考级别,代表被其它帧参考情况,u(2)= 11 = 3最(0为无参考,详见规范)
nal_unit_type是该帧的类型,为剩下的5位,u(5)= 0 0111 = 7
目前类型有:
//H264定义的类型 values for nal_unit_type
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
#if (MVC_EXTENSION_ENABLE)
NALU_TYPE_PREFIX = 14,
NALU_TYPE_SUB_SPS = 15,
NALU_TYPE_SLC_EXT = 20,
NALU_TYPE_VDRD = 24 // View and Dependency Representation Delimiter NAL Unit
#endif
} NaluType;
可以看出是NALU_TYPE_SPS 即sequence parameter sets
profile_idc的u(8)则是后面的64转化为十进制则是100,
66 Baseline
77 Main
88 Extended
100 High (FRExt)
110 High 10 (FRExt)
122 High 4:2:2 (FRExt)
144 High 4:4:4 (FRExt)
100是High (FRExt)
“level_idc则是0D是13,seq_parameter_set_id的ue(v),则指到AC了,这是哥伦布编码,答案是0,这个是怎么算出来的?“
就不太懂了。互相帮忙吧。
参考
《新一代视频压缩编码标准 毕厚杰 第7章 H264的句法和语义》
H264/AVC视频编解码技术详解十一、H264的Slice Header解析
H264/AVC视频编解码技术详解十二、解析H264码流的宏块结构(上)
H264/AVC视频编解码技术详解十二、解析H264码流的宏块结构(下):H264帧内编码宏块的预测结构
我们已经知道,整个H264的码流结构可以分为两层:网络抽象层NAL和视频编码层VCL。在NAL层,H264的码流表示为一系列的NAL Units,不同的NAL Units中包含不同类型的语法元素。前面两篇中所解析的序列参数集SPS和图像参数集PPS就是其中重要的两个部分,其中包含了控制解码过程的一些通用的参数。
实际保存原始视频的图像数据的部分保存在其他的VCL层的NAL Units中。这部分数据在码流中称作是条带(Slice)。一个Slice包含一帧图像的部分或全部数据,换言之,一帧视频图像可以编码为一个或若干个Slice。一个Slice最少包含一个宏块,最多包含整帧图像的数据。在不同的编码实现中,同一帧图像中所构成的Slice数目不一定相同。
在H264中设计Slice的目的主要在于防止误码的扩散。因为不同的slice之间,其解码 *** 作是独立的。某一个slice的解码过程所参考的数据(例如预测编码)不能越过slice的边界。
根据码流中不同的数据类型,H264标准中共定义了5种Slice类型:
在I slice中只包含I宏块,不能包含P或B宏块;在P和B slice中,除了相应的P和B类型宏块之外,还可以包含I类型宏块。
每一个Slice总体来看都由两部分组成,一部分作为Slice header,用于保存Slice的总体信息(如当前Slice的类型等),另一部分为Slice body,通常是一组连续的宏块结构(或者宏块跳过信息),如下图所示:
Slice header中主要保存了当前slice的一些全局的信息,slice body中的宏块在进行解码时需依赖这些信息。其中比较常见的一些语法元素有:
IDR 图像时, slice_type 等于 2, 4, 7, 9。
在已经实现了一个slice的header部分之后,下面的工作将是研究如何解析一个slice的主体,即Slice Body部分。一个Slice的body部分主要是一个个的宏块结构Macroblock组成,此外还存在一些辅助的信息。标准文档中规定的slice_data()结构如下图:
从文档中我们可以看出,Slice_data结构中独立的语法元素并不多,主要只有以下几个:
上述的几个语法元素毫无疑问仅仅占用了全部数据很少的一部分,其他大部分的数据都包含在宏块结构中,即上表中的 macroblock_layer()结构 。
从上表中我们可以看出,一个Slice结构中宏块实际上占据了绝大部分。在标准中一个宏块的结构定义为下表:
在一个宏块中,最开始的语法元素为宏块的类型:mb_type。从表中我们可以看出,根据mb_type的值是否等于I_PCM,整个解析方法分为两大类:PCM类型和非PCM类型,判断依据是当mb_type为25时为I_PCM模式,否则为非I_PCM模式。
当这个宏块为I_PCM模式时,宏块中以差分编码的形式保存宏块原始的像素值。此时存在如下几个语法元素:
除了mb_type等于25时可以确定为I_PCM格式之外,其他的mb_type值可能根据帧类型(或slice类型)的不同而不同。比如对于I slice,mb_type的非PCM模式可以选择0~24这些值之一;对于P slice,mb_type只能取0~4这5个值;对于B slice,mb_type可以取0~22这些值之一。目前我们所处理的码流全部由I帧构成,因此我们暂时只考虑I slice的情况。下图是标准文档中规定的I slice的mb_type列表的一部分,完整列表在协议文档的表7-11中:
从上表中我们可以看出,mb_type不仅仅表示了宏块的分割方式,还包含了一些其他的附加信息,如帧内预测模式、亮度和色度分量的coded_block_pattern。当帧内预测使用16×16模式时,宏块整个宏块的预测信息相同,因此不需要为各个子宏块分别指定预测模式,这样可以有效减少消耗的码流。
该语法元素为一个标识位,用于表示在环路滤波之前,预测残差的变换系数解码时依照的尺寸。当该标识位为1时,预测残差按照8×8像素块进行解码;当该标志位不存在或者为0时,预测残差按照4×4像素块进行解码。
coded_block_pattern语法元素常简称做cbp,用于表示当前宏块内的4个8×8子块编码对其中的哪个的残差系数进行编码。值得注意的是该语法元素仅仅在宏块为非I_16x16模式时才存在,因为在I_16x16模式时cbp的有关信息已经在mb_type中体现。
mb_qp_delta表示宏块层的量化参数偏移值,取值范围为[-26, 25]。我们在前面已经在PPS中获取了整个序列的量化参数初始值(由pic_init_qp_minus26计算),在slice header中获取slice层的量化参数偏移slice_qp_delta,因此每一个slice第一个宏块的量化参数可通过下面的公式计算:
从第二个宏块开始,每个宏块实际量化参数的计算方法为:
在以H264格式编码的视频码流中,宏块结构必然包含预测结构(I_PCM模式除外),该结构中包含了像素块的预测模式等信息。对于不同预测模式的宏块,其预测结构是不同的。从上篇的宏块结构中,可以看出,对于部分模式,预测信息保存于mb_pred()结构中,而对于另一部分模式则采用sub_mb_pred()结构。
在我们本系列的H264分析器SimpleH264Analyzer项目中默认的全I帧测试码流中,我们所分析的第一个IDR帧的第一个宏块,其mb_type为I_NxN。实际上,对于除了I_PCM模式之外的所有Intra宏块,其预测结构均采用mb_pred()结构。
在标准文档中,mb_pred()的定义如下表所示(只看Intra模式下):
从表中可以看出,Intra预测模式的结构主要有两组,分别表示4×4和8×8模式,每一组包含两个元素,分别表示预测模式标识位和预测模式值,以及最后的色度分量预测模式。
H264从1999年开始,到2003年形成草案,最后在2007年定稿有待核实。在ITU的标准⾥称为H264,在MPEG的标准⾥是MPEG-4的⼀个组成部分–MPEG-4 Part 10,⼜叫Advanced Video Codec,因此常常称为MPEG-4 AVC或直接叫AVC。
NALU(Network Abstract Layer Unit)全称为网络抽象层单元。
对于视频⽂件来说,视频由单张图⽚帧所组成,⽐如每秒25帧,但是图⽚帧的像素块之间存在相似性,因此视频帧图像可以进⾏图像压缩;H264采⽤了1616的分块⼤⼩,对视频帧图像进⾏相似⽐较和压缩编码。
H264使⽤帧内压缩和帧间压缩的⽅式提⾼编码压缩率;H264采⽤了独特的I帧、P帧和B帧策略来实现,连续帧之间的压缩。
H264帧的分类:
H264每一帧的结构由组(GOP,group of pictures)、图⽚(pictrue)、⽚(Slice)、宏块(Macroblock)、⼦块(subblock)五个层次组成。
GOP (图像组)主要⽤作形容⼀个IDR(Instantaneous Decoding Refresh,即时解码刷新)帧 到下⼀个IDR帧之间的间隔了多少个帧。
一个图像组的第一个图像叫做IDR图像,IDR图像都是I帧图像。
IDR的核⼼作⽤是,是为了解码的重同步,当解码器解码到 IDR 图像时,⽴即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始⼀个新的序列。这样,如果前⼀个序列出现重⼤错误,在这⾥可以获得重新同步的机会。IDR图像之后的图像永远不会使⽤IDR之前的图像的数据来解码。
SPS:序列参数集(Sequence parameter set)SPS中保存了⼀组编码视频序列(Coded video sequence)的全局参数。
PPS:图像参数集(Picture parameter set) ,对应的是⼀个序列中某⼀幅图像或者某⼏幅图像的参数。
I帧:帧内编码帧,可独⽴解码⽣成完整的图⽚。
P帧: 前向预测编码帧,需要参考其前⾯的⼀个I 或者B 来⽣成⼀张完整的图⽚。
B帧: 双向预测内插编码帧,则要参考其前⼀个I或者P帧及其后⾯的⼀个P帧来⽣成⼀张完整的图⽚。
发I帧之前,⾄少要发⼀次SPS和PPS。
H264原始码流(裸流)是由⼀个接⼀个NALU组成,它的功能分为两层,VCL(video codec layer,视频编码层)和NAL(Network Abstract Layer,网络抽象层)。
在VCL进⾏数据传输或存储之前,这些编码的VCL数据,被映射或封装进NAL单元(NALU)。⼀个NALU = ⼀组对应于视频编码的NALU头部信息 + ⼀个原始字节序列负荷(RBSP,Raw Byte Sequence Payload)
NALU结构单元的主体结构如下所示;⼀个原始的H264 NALU单元通常由[StartCode] [NALU Header] [NALU Payload]三部分组成,其中 Start Code ⽤于标示这是⼀个NALU 的开始,必须是"00 00 00 01" 或"00 00 01",除此之外基本相当于⼀个NAL header + RBSP;
FFmpeg解复⽤后,MP4、FLV、TS⽂件读取出来的packet是不带startcode,值得注意的是TS封装格式无start code、SPS、PPS也是可以正常播放的。
每个NALU是⼀个⼀定语法元素的可变⻓字节字符串,包括包含⼀个字节的头信息(⽤来表示数据类型),以及若⼲整数字节的负荷数据。
NALU头信息(1Byte):
H264标准指出,当数据流是储存在介质上时,在每个NALU 前添加起始码:0x000001 或0x00000001,⽤来指示⼀个NALU 的起始位置。
H264有两种封装格式:
命令
结果:
前言:
为什么需要编码呢?比如当前屏幕是1280720一秒24张那么我们一秒的视频数据是
1280720(位像素)24(张) / 8(1字节8位)(结果:B) / 1024(结果:KB) / 1024 (结果:MB) = 264MB
一秒的数据有264MB数据量。1分钟就会有100多MB。这对用户来说真心是灾难。所以现在我们需要一种压缩方式减小数据的大小在更低 比特率(bps)的情况下依然提供清晰的视频。
H264: H264/AVC是广泛采用的一种编码方式。我们这边会带大家了解。从大到小排序依次是 序列,图像,NALU,片,宏块,亚宏块,块,像素。
问题背景:
前面在讲封装格式过程中,都有一个章节讲解如何将H264的NALU单元如何打包到TS、FLV、RTP中,解装刚好相反,怎么从这些封装格式里面解析出一个个NALU单元。NALU即是编码器的输出数据又是解码器的输入数据,所以在封装和传输时,我们一般处理对象就是NALU,至于NALU内部到底是什么则很少关心。甚至我们在编解码时,我们只需要初始化好x264编码库,然后输入YUV数据,它就会给你经过一系列压缩算法后输出NALU,或者将NALU输入到x264解码库就会输出YUV数据。
这篇文章就初步带你看下NALU能传输那些数据,NALU的类型和结构以及H264码流的层次,最后通过分析工具分析下裸码流记性验证,你可以选择感兴趣章节阅读。
NALU结构:
H264的基本流(elementary stream),也叫裸流(没有加格式封装),就是一系列NALU的集合,如下图所示:
用Notepad十六进制形式打开,以annexb格式存储的h264裸流文件内容:
NALU结构分为两层,包含了视频编码层(VCL)和网络适配层(NAL):
视频编码层(VCL即Video Coding Layer) :负责高效的视频内容表示,这是核心算法引擎,其中对宏块、片的处理都包含在这个层级上,它输出的数据是SODB;
网络适配层(NAL即Network Abstraction Layer) :以网络所要求的恰当方式对数据进行打包和发送,比较简单,先报VCL吐出来的数据SODB进行字节对齐,形成RBSP,最后再RBSP数据前面加上NAL头则组成一个NALU单元。
分层目的:
这样做的目的:VCL只负责视频的信号处理,包含压缩,量化等处理,NAL解决编码后数据的网络传输,这样可以将VCL和NAL的处理放到不同平台来处理,可以减少因为网络环境不同对VCL的比特流进行重构和重编码;
NLAU结构:
其实NALU的承载数据真实并不是RBSP(Raw Byte Sequence Playload)而是EBSP即(Extent Byte Sequence Payload),EBSP和RBSP的区别就是在 RBSP里面加入防伪起始码字节(0x03),因为H264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置,那么RBSP数据内部是有可能含有这种字节序列的,为了防止解析错误,所以在RBSP数据流里面碰到0x 00 00 00 01的0x01前面就会加上0x03,解码时将NALU的EBSP中的0x03去掉成为RBSP,称为脱壳 *** 作。
原始字节序列负载 RBSP即Raw Byte Sequence Playload,因为VCL输出的 原始数据比特流 SODB即String Of Data Bits,其长度不一定是8bit的整数倍,为了凑成整数个字节,往往需要对SODB最后一个字节进行填充形成RBSP,所以从SODB到RBSP的示意图如下:
填充方式就是对VCL的输出数据进行8bit进行切分,最后一个不满8bit的字节第一bit位置1,然后后面缺省的bit置0即可
具体填充语法见下文:
原来文档中的解释:
主要的意思我的理解如下:
其中H264规范规定,编码器吐出来的数据需要在每个NALU添加起始码:0x00 00 01或者0x00 00 00 01,用来指示一个NALU的起始和终止位置。
所以H264编码器输出的码流中每个帧开头3-4字节的start code起始码为0x00 00 01或者0x00 00 00 01。
上面我们分析了NALU的结构以及每层输出数据的处理方法,但是对于NALU的RBSP数据二进制表示的什么含义并不清楚,下面分析下NALU的类型。
1 NALU Header
头信息协议如上图。
举例说明:
这其中NALU的RBSP除了能承载真实的视频压缩数据,还能传输编码器的配置信息,其中能传输视频压缩数据的为slice。
那么如果NLAU传输视频压缩数据时,编码器没有开启DP(数据分割)机制,则一个片就是一个NALU,一个 NALU 也就是一个片。否则,一个片由三个 NALU 组成,即DPA、DPB和DPC,对应的nal_unit_type 类型为 2、3和4。
通常情况我们看到的NLAU类型就是SPS、PPS、SEI、IDR的slice、非IDR这几种。
上面站在NALU的角度看了NALU的类型、结构、数据来源、分层处理的原因等,其中NLAU最主要的目的就是传输视频数据压缩结果。那么站在对数据本身的理解上,我们看下H264码流的层次结构。
H264层次结构:
其实为了理解H264是如何看待视频数据,先要了解下视频的形成过程。其实你把多副连续的有关联图像连续播就可以形成视频,这主要利用了人视觉系统的暂留效应,当把连续的以每秒25张的速度播放,人眼基本就感觉是连续的视频了。动画片就是这个原理:一张图像里面相邻的区域或者一段时间内连续图像的相同位置,像素、亮度、色温差别比较小,所以视频压缩本质就是利于这种空间冗余和时间上冗余进行编码,我们可以选取一段时间第一幅图像的YUV值,后面的只需要记录和这个的完整图像的差别即可,同时即使记录一副图像的YUV值,当有镜头完全切换时,我们又选取切换后的第一张作为基本图像,后面有一篇文章回讲述下目前视频压缩的基本原理。
所以从这里面就可以引申以下几个概念:
所以视频流分析的对象可以用下面的描述:
如果站在数据的角度分析NALU的层次关系,如下图:
这里视频帧被划分为一个片或者多个片,其中slice数据主要就是通过NLAU进行传输,其中slice数据又是由:
一个Slice = Silce + Slice Data
一帧跟 NALU 的关联 :
一帧经过 H264 编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是 NALU 了,我们可以来看看 NALU 跟片的关系(slice)。
引用自: >
以上就是关于H264的NAL介绍全部的内容,包括:H264的NAL介绍、视频开发基础知识、H264码流解析等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)